OSDN Git Service

(OBJC_ERR_BAD_STATE): New error code.
[pf3gnuchains/gcc-fork.git] / gcc / flow.c
index 65ba69f..0689d60 100644 (file)
@@ -1,5 +1,5 @@
 /* Data flow analysis for GNU compiler.
-   Copyright (C) 1987, 1988, 1992 Free Software Foundation, Inc.
+   Copyright (C) 1987, 88, 92-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 contains the data flow analysis pass of the compiler.
@@ -116,13 +117,18 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "hard-reg-set.h"
 #include "flags.h"
 #include "output.h"
+#include "except.h"
 
 #include "obstack.h"
 #define obstack_chunk_alloc xmalloc
 #define obstack_chunk_free free
 
-extern int xmalloc ();
-extern void free ();
+/* The contents of the current function definition are allocated
+   in this obstack, and all are freed at the end of the function.
+   For top-level functions, this is temporary_obstack.
+   Separate obstacks are made for nested functions.  */
+
+extern struct obstack *function_obstack;
 
 /* List of labels that must never be deleted.  */
 extern rtx forced_labels;
@@ -141,7 +147,7 @@ static int max_uid_for_flow;
    This is set up by find_basic_blocks and used there and in life_analysis,
    and then freed.  */
 
-static short *uid_block_number;
+static int *uid_block_number;
 
 /* INSN_VOLATILE (insn) is 1 if the insn refers to anything volatile.  */
 
@@ -156,7 +162,8 @@ int n_basic_blocks;
 
 int max_regno;
 
-/* Maximum number of SCRATCH rtx's used in any basic block of this function. */
+/* Maximum number of SCRATCH rtx's used in any basic block of this
+   function.  */
 
 int max_scratch;
 
@@ -164,50 +171,9 @@ int max_scratch;
 
 static int num_scratch;
 
-/* Indexed by n, gives number of basic block that  (REG n) is used in.
-   If the value is REG_BLOCK_GLOBAL (-2),
-   it means (REG n) is used in more than one basic block.
-   REG_BLOCK_UNKNOWN (-1) means it hasn't been seen yet so we don't know.
-   This information remains valid for the rest of the compilation
-   of the current function; it is used to control register allocation.  */
-
-short *reg_basic_block;
-
-/* Indexed by n, gives number of times (REG n) is used or set, each
-   weighted by its loop-depth.
-   This information remains valid for the rest of the compilation
-   of the current function; it is used to control register allocation.  */
-
-int *reg_n_refs;
-
-/* Indexed by N, gives number of places register N dies.
-   This information remains valid for the rest of the compilation
-   of the current function; it is used to control register allocation.  */
-
-short *reg_n_deaths;
-
-/* Indexed by N, gives 1 if that reg is live across any CALL_INSNs.
-   This information remains valid for the rest of the compilation
-   of the current function; it is used to control register allocation.  */
-
-int *reg_n_calls_crossed;
-
-/* Total number of instructions at which (REG n) is live.
-   The larger this is, the less priority (REG n) gets for
-   allocation in a real register.
-   This information remains valid for the rest of the compilation
-   of the current function; it is used to control register allocation.
+/* Indexed by n, giving various register information */
 
-   local-alloc.c may alter this number to change the priority.
-
-   Negative values are special.
-   -1 is used to mark a pseudo reg which has a constant or memory equivalent
-   and is used infrequently enough that it should not get a hard register.
-   -2 is used to mark a pseudo reg for a parameter, when a frame pointer
-   is not required.  global-alloc.c makes an allocno for this but does
-   not try to assign a hard register to it.  */
-
-int *reg_live_length;
+reg_info *reg_n_info;
 
 /* Element N is the next insn that uses (hard or pseudo) register number N
    within the current basic block; or zero, if there is no such insn.
@@ -282,20 +248,27 @@ static rtx last_mem_set;
 static HARD_REG_SET elim_reg_set;
 
 /* Forward declarations */
-static void find_basic_blocks ();
-static void life_analysis ();
-static void mark_label_ref ();
-void allocate_for_life_analysis (); /* Used also in stupid_life_analysis */
-static void init_regset_vector ();
-static void propagate_block ();
-static void mark_set_regs ();
-static void mark_used_regs ();
-static int insn_dead_p ();
-static int libcall_dead_p ();
-static int try_pre_increment ();
-static int try_pre_increment_1 ();
-static rtx find_use_as_address ();
-void dump_flow_info ();
+static void find_basic_blocks          PROTO((rtx, rtx));
+static int jmp_uses_reg_or_mem         PROTO((rtx));
+static void mark_label_ref             PROTO((rtx, rtx, int));
+static void life_analysis              PROTO((rtx, int));
+void allocate_for_life_analysis                PROTO((void));
+static void init_regset_vector         PROTO((regset *, int, int, struct obstack *));
+static void propagate_block            PROTO((regset, rtx, rtx, int, 
+                                              regset, int));
+static rtx flow_delete_insn            PROTO((rtx));
+static int insn_dead_p                 PROTO((rtx, regset, int));
+static int libcall_dead_p              PROTO((rtx, regset, rtx, rtx));
+static void mark_set_regs              PROTO((regset, regset, rtx,
+                                              rtx, regset));
+static void mark_set_1                 PROTO((regset, regset, rtx,
+                                              rtx, regset));
+static void find_auto_inc              PROTO((regset, rtx, rtx));
+static void mark_used_regs             PROTO((regset, regset, rtx, int, rtx));
+static int try_pre_increment_1         PROTO((rtx));
+static int try_pre_increment           PROTO((rtx, rtx, HOST_WIDE_INT));
+static rtx find_use_as_address         PROTO((rtx, rtx, HOST_WIDE_INT));
+void dump_flow_info                    PROTO((FILE *));
 \f
 /* Find basic blocks of the current function and perform data flow analysis.
    F is the first insn of the function and NREGS the number of register numbers
@@ -309,13 +282,14 @@ flow_analysis (f, nregs, file)
 {
   register rtx insn;
   register int i;
+  rtx nonlocal_label_list = nonlocal_label_rtx_list ();
 
 #ifdef ELIMINABLE_REGS
   static struct {int from, to; } eliminables[] = ELIMINABLE_REGS;
 #endif
 
   /* Record which registers will be eliminated.  We use this in
-     mark_used_regs. */
+     mark_used_regs.  */
 
   CLEAR_HARD_REG_SET (elim_reg_set);
 
@@ -340,10 +314,16 @@ flow_analysis (f, nregs, file)
        if (INSN_UID (insn) > max_uid_for_flow)
          max_uid_for_flow = INSN_UID (insn);
        if (code == CODE_LABEL
-           || (prev_code != INSN && prev_code != CALL_INSN
-               && prev_code != CODE_LABEL
-               && GET_RTX_CLASS (code) == 'i'))
+           || (GET_RTX_CLASS (code) == 'i'
+               && (prev_code == JUMP_INSN
+                   || (prev_code == CALL_INSN
+                       && nonlocal_label_list != 0)
+                   || prev_code == BARRIER)))
          i++;
+
+       if (code == CALL_INSN && find_reg_note (insn, REG_RETVAL, NULL_RTX))
+         code = INSN;
+
        if (code != NOTE)
          prev_code = code;
       }
@@ -364,11 +344,11 @@ flow_analysis (f, nregs, file)
   basic_block_drops_in = (char *) alloca (n_basic_blocks);
   basic_block_loop_depth = (short *) alloca (n_basic_blocks * sizeof (short));
   uid_block_number
-    = (short *) alloca ((max_uid_for_flow + 1) * sizeof (short));
+    = (int *) alloca ((max_uid_for_flow + 1) * sizeof (int));
   uid_volatile = (char *) alloca (max_uid_for_flow + 1);
   bzero (uid_volatile, max_uid_for_flow + 1);
 
-  find_basic_blocks (f);
+  find_basic_blocks (f, nonlocal_label_list);
   life_analysis (f, nregs);
   if (file)
     dump_flow_info (file);
@@ -381,11 +361,13 @@ flow_analysis (f, nregs, file)
 /* Find all basic blocks of the function whose first insn is F.
    Store the correct data in the tables that describe the basic blocks,
    set up the chains of references for each CODE_LABEL, and
-   delete any entire basic blocks that cannot be reached.  */
+   delete any entire basic blocks that cannot be reached.
+
+   NONLOCAL_LABEL_LIST is the same local variable from flow_analysis.  */
 
 static void
-find_basic_blocks (f)
-     rtx f;
+find_basic_blocks (f, nonlocal_label_list)
+     rtx f, nonlocal_label_list;
 {
   register rtx insn;
   register int i;
@@ -393,8 +375,17 @@ find_basic_blocks (f)
   register char *block_marked = (char *) alloca (n_basic_blocks);
   /* List of label_refs to all labels whose addresses are taken
      and used as data.  */
-  rtx label_value_list = 0;
+  rtx label_value_list;
+  int label_value_list_marked_live;
+  rtx x, note;
+  enum rtx_code prev_code, code;
+  int depth, pass;
+
+  pass = 1;
+ restart:
 
+  label_value_list = 0;
+  label_value_list_marked_live = 0;
   block_live_static = block_live;
   bzero (block_live, n_basic_blocks);
   bzero (block_marked, n_basic_blocks);
@@ -403,82 +394,91 @@ find_basic_blocks (f)
   if (n_basic_blocks > 0)
     block_live[0] = 1;
 
-  /* Initialize the ref chain of each label to 0.  */
-  /* Record where all the blocks start and end and their depth in loops.  */
-  /* For each insn, record the block it is in.  */
-  /* Also mark as reachable any blocks headed by labels that
-     must not be deleted.  */
+  /* Initialize the ref chain of each label to 0.  Record where all the
+     blocks start and end and their depth in loops.  For each insn, record
+     the block it is in.   Also mark as reachable any blocks headed by labels
+     that must not be deleted.  */
 
-  {
-    register RTX_CODE prev_code = JUMP_INSN;
-    register RTX_CODE code;
-    int depth = 1;
+  for (insn = f, i = -1, prev_code = JUMP_INSN, depth = 1;
+       insn; insn = NEXT_INSN (insn))
+    {
+      code = GET_CODE (insn);
+      if (code == NOTE)
+       {
+         if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
+           depth++;
+         else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
+           depth--;
+       }
 
-    for (insn = f, i = -1; insn; insn = NEXT_INSN (insn))
-      {
-       code = GET_CODE (insn);
-       if (code == NOTE)
-         {
-           if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
-             depth++;
-           else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
-             depth--;
-         }
-       else if (code == CODE_LABEL
-                || (prev_code != INSN && prev_code != CALL_INSN
-                    && prev_code != CODE_LABEL
-                    && GET_RTX_CLASS (code) == 'i'))
-         {
-           basic_block_head[++i] = insn;
-           basic_block_end[i] = insn;
-           basic_block_loop_depth[i] = depth;
-           if (code == CODE_LABEL)
-             {
+      /* A basic block starts at label, or after something that can jump.  */
+      else if (code == CODE_LABEL
+              || (GET_RTX_CLASS (code) == 'i'
+                  && (prev_code == JUMP_INSN
+                      || (prev_code == CALL_INSN
+                          && nonlocal_label_list != 0
+                          && ! find_reg_note (insn, REG_RETVAL, NULL_RTX))
+                      || prev_code == BARRIER)))
+       {
+         basic_block_head[++i] = insn;
+         basic_block_end[i] = insn;
+         basic_block_loop_depth[i] = depth;
+
+         if (code == CODE_LABEL)
+           {
                LABEL_REFS (insn) = insn;
                /* Any label that cannot be deleted
                   is considered to start a reachable block.  */
                if (LABEL_PRESERVE_P (insn))
                  block_live[i] = 1;
              }
-         }
-       else if (GET_RTX_CLASS (code) == 'i')
-         {
-           basic_block_end[i] = insn;
-           basic_block_loop_depth[i] = depth;
-         }
+       }
 
-       /* Make a list of all labels referred to other than by jumps.  */
-       if (code == INSN || code == CALL_INSN)
-         {
-           rtx note = find_reg_note (insn, REG_LABEL, 0);
-           if (note != 0)
+      else if (GET_RTX_CLASS (code) == 'i')
+       {
+         basic_block_end[i] = insn;
+         basic_block_loop_depth[i] = depth;
+       }
+
+      if (GET_RTX_CLASS (code) == 'i')
+       {
+         /* Make a list of all labels referred to other than by jumps.  */
+         for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+           if (REG_NOTE_KIND (note) == REG_LABEL)
              label_value_list = gen_rtx (EXPR_LIST, VOIDmode, XEXP (note, 0),
                                          label_value_list);
-         }
+       }
 
-       BLOCK_NUM (insn) = i;
-       if (code != NOTE)
-         prev_code = code;
-      }
-    if (i + 1 != n_basic_blocks)
-      abort ();
-  }
+      BLOCK_NUM (insn) = i;
+
+      if (code != NOTE)
+       prev_code = code;
+    }
+
+  /* During the second pass, `n_basic_blocks' is only an upper bound.
+     Only perform the sanity check for the first pass, and on the second
+     pass ensure `n_basic_blocks' is set to the correct value.  */
+  if (pass == 1 && i + 1 != n_basic_blocks)
+    abort ();
+  n_basic_blocks = i + 1;
+
+  for (x = forced_labels; x; x = XEXP (x, 1))
+    if (! LABEL_REF_NONLOCAL_P (x))
+      block_live[BLOCK_NUM (XEXP (x, 0))] = 1;
+
+  for (x = exception_handler_labels; x; x = XEXP (x, 1))
+    block_live[BLOCK_NUM (XEXP (x, 0))] = 1;
 
   /* Record which basic blocks control can drop in to.  */
 
-  {
-    register int i;
-    for (i = 0; i < n_basic_blocks; i++)
-      {
-       register rtx insn = PREV_INSN (basic_block_head[i]);
-       /* TEMP1 is used to avoid a bug in Sequent's compiler.  */
-       register int temp1;
-       while (insn && GET_CODE (insn) == NOTE)
-         insn = PREV_INSN (insn);
-       temp1 = insn && GET_CODE (insn) != BARRIER;
-       basic_block_drops_in[i] = temp1;
-      }
-  }
+  for (i = 0; i < n_basic_blocks; i++)
+    {
+      for (insn = PREV_INSN (basic_block_head[i]);
+          insn && GET_CODE (insn) == NOTE; insn = PREV_INSN (insn))
+       ;
+
+      basic_block_drops_in[i] = insn && GET_CODE (insn) != BARRIER;
+    }
 
   /* Now find which basic blocks can actually be reached
      and put all jump insns' LABEL_REFS onto the ref-chains
@@ -487,27 +487,101 @@ find_basic_blocks (f)
   if (n_basic_blocks > 0)
     {
       int something_marked = 1;
+      int deleted;
 
-      /* Find all indirect jump insns and mark them as possibly jumping
-        to all the labels whose addresses are explicitly used.
-        This is because, when there are computed gotos,
-        we can't tell which labels they jump to, of all the possibilities.  */
+      /* Find all indirect jump insns and mark them as possibly jumping to all
+        the labels whose addresses are explicitly used.  This is because,
+        when there are computed gotos, we can't tell which labels they jump
+        to, of all the possibilities.
+
+        Tablejumps and casesi insns are OK and we can recognize them by
+        a (use (label_ref)).  */
 
       for (insn = f; insn; insn = NEXT_INSN (insn))
-       if (GET_CODE (insn) == JUMP_INSN
-           && GET_CODE (PATTERN (insn)) == SET
-           && SET_DEST (PATTERN (insn)) == pc_rtx
-           && GET_CODE (SET_SRC (PATTERN (insn))) == REG)
+       if (GET_CODE (insn) == JUMP_INSN)
          {
-           rtx x;
-           for (x = label_value_list; x; x = XEXP (x, 1))
-             mark_label_ref (gen_rtx (LABEL_REF, VOIDmode, XEXP (x, 0)),
+           rtx pat = PATTERN (insn);
+           int computed_jump = 0;
+
+           if (GET_CODE (pat) == PARALLEL)
+             {
+               int len = XVECLEN (pat, 0);
+               int has_use_labelref = 0;
+
+               for (i = len - 1; i >= 0; i--)
+                 if (GET_CODE (XVECEXP (pat, 0, i)) == USE
+                     && (GET_CODE (XEXP (XVECEXP (pat, 0, i), 0))
+                         == LABEL_REF))
+                   has_use_labelref = 1;
+
+               if (! has_use_labelref)
+                 for (i = len - 1; i >= 0; i--)
+                   if (GET_CODE (XVECEXP (pat, 0, i)) == SET
+                       && SET_DEST (XVECEXP (pat, 0, i)) == pc_rtx
+                       && jmp_uses_reg_or_mem (SET_SRC (XVECEXP (pat, 0, i))))
+                     computed_jump = 1;
+             }
+           else if (GET_CODE (pat) == SET
+                    && SET_DEST (pat) == pc_rtx
+                    && jmp_uses_reg_or_mem (SET_SRC (pat)))
+             computed_jump = 1;
+                   
+           if (computed_jump)
+             {
+               if (label_value_list_marked_live == 0)
+                 {
+                   label_value_list_marked_live = 1;
+
+                   /* This could be made smarter by only considering
+                      these live, if the computed goto is live.  */
+
+                   /* Don't delete the labels (in this function) that
+                      are referenced by non-jump instructions.  */
+
+                   for (x = label_value_list; x; x = XEXP (x, 1))
+                     if (! LABEL_REF_NONLOCAL_P (x))
+                       block_live[BLOCK_NUM (XEXP (x, 0))] = 1;
+                 }
+
+               for (x = label_value_list; x; x = XEXP (x, 1))
+                 mark_label_ref (gen_rtx (LABEL_REF, VOIDmode, XEXP (x, 0)),
+                                 insn, 0);
+
+               for (x = forced_labels; x; x = XEXP (x, 1))
+                 mark_label_ref (gen_rtx (LABEL_REF, VOIDmode, XEXP (x, 0)),
                              insn, 0);
-           for (x = forced_labels; x; x = XEXP (x, 1))
+             }
+         }
+
+      /* Find all call insns and mark them as possibly jumping
+        to all the nonlocal goto handler labels.  */
+
+      for (insn = f; insn; insn = NEXT_INSN (insn))
+       if (GET_CODE (insn) == CALL_INSN
+           && ! find_reg_note (insn, REG_RETVAL, NULL_RTX))
+         {
+           for (x = nonlocal_label_list; x; x = XEXP (x, 1))
              mark_label_ref (gen_rtx (LABEL_REF, VOIDmode, XEXP (x, 0)),
                              insn, 0);
+
+           /* ??? This could be made smarter:
+              in some cases it's possible to tell that certain
+              calls will not do a nonlocal goto.
+
+              For example, if the nested functions that do the
+              nonlocal gotos do not have their addresses taken, then
+              only calls to those functions or to other nested
+              functions that use them could possibly do nonlocal
+              gotos.  */
          }
 
+      /* All blocks associated with labels in label_value_list are
+        trivially considered as marked live, if the list is empty.
+        We do this to speed up the below code.  */
+
+      if (label_value_list == 0)
+       label_value_list_marked_live = 1;
+
       /* Pass over all blocks, marking each block that is reachable
         and has not yet been marked.
         Keep doing this until, in one pass, no blocks have been marked.
@@ -527,41 +601,110 @@ find_basic_blocks (f)
                insn = basic_block_end[i];
                if (GET_CODE (insn) == JUMP_INSN)
                  mark_label_ref (PATTERN (insn), insn, 0);
+
+               if (label_value_list_marked_live == 0)
+                 /* Now that we know that this block is live, mark as
+                    live, all the blocks that we might be able to get
+                    to as live.  */
+
+                 for (insn = basic_block_head[i];
+                      insn != NEXT_INSN (basic_block_end[i]);
+                      insn = NEXT_INSN (insn))
+                   {
+                     if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+                       {
+                         for (note = REG_NOTES (insn);
+                              note;
+                              note = XEXP (note, 1))
+                           if (REG_NOTE_KIND (note) == REG_LABEL)
+                             {
+                               x = XEXP (note, 0);
+                               block_live[BLOCK_NUM (x)] = 1;
+                             }
+                       }
+                   }
              }
        }
 
+      /* ??? See if we have a "live" basic block that is not reachable.
+        This can happen if it is headed by a label that is preserved or
+        in one of the label lists, but no call or computed jump is in
+        the loop.  It's not clear if we can delete the block or not,
+        but don't for now.  However, we will mess up register status if
+        it remains unreachable, so add a fake reachability from the
+        previous block.  */
+
+      for (i = 1; i < n_basic_blocks; i++)
+       if (block_live[i] && ! basic_block_drops_in[i]
+           && GET_CODE (basic_block_head[i]) == CODE_LABEL
+           && LABEL_REFS (basic_block_head[i]) == basic_block_head[i])
+         basic_block_drops_in[i] = 1;
+
       /* Now delete the code for any basic blocks that can't be reached.
         They can occur because jump_optimize does not recognize
         unreachable loops as unreachable.  */
 
+      deleted = 0;
       for (i = 0; i < n_basic_blocks; i++)
        if (!block_live[i])
          {
+           deleted++;
+
+           /* Delete the insns in a (non-live) block.  We physically delete
+              every non-note insn except the start and end (so
+              basic_block_head/end needn't be updated), we turn the latter
+              into NOTE_INSN_DELETED notes.
+              We use to "delete" the insns by turning them into notes, but
+              we may be deleting lots of insns that subsequent passes would
+              otherwise have to process.  Secondly, lots of deleted blocks in
+              a row can really slow down propagate_block since it will
+              otherwise process insn-turned-notes multiple times when it
+              looks for loop begin/end notes.  */
+           if (basic_block_head[i] != basic_block_end[i])
+             {
+               /* It would be quicker to delete all of these with a single
+                  unchaining, rather than one at a time, but we need to keep
+                  the NOTE's.  */
+               insn = NEXT_INSN (basic_block_head[i]);
+               while (insn != basic_block_end[i])
+                 {
+                   if (GET_CODE (insn) == BARRIER)
+                     abort ();
+                   else if (GET_CODE (insn) != NOTE)
+                     insn = flow_delete_insn (insn);
+                   else
+                     insn = NEXT_INSN (insn);
+                 }
+             }
            insn = basic_block_head[i];
-           while (1)
+           if (GET_CODE (insn) != NOTE)
              {
+               /* Turn the head into a deleted insn note.  */
                if (GET_CODE (insn) == BARRIER)
                  abort ();
-               if (GET_CODE (insn) != NOTE)
-                 {
-                   PUT_CODE (insn, NOTE);
-                   NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
-                   NOTE_SOURCE_FILE (insn) = 0;
-                 }
-               if (insn == basic_block_end[i])
-                 {
-                   /* BARRIERs are between basic blocks, not part of one.
-                      Delete a BARRIER if the preceding jump is deleted.
-                      We cannot alter a BARRIER into a NOTE
-                      because it is too short; but we can really delete
-                      it because it is not part of a basic block.  */
-                   if (NEXT_INSN (insn) != 0
-                       && GET_CODE (NEXT_INSN (insn)) == BARRIER)
-                     delete_insn (NEXT_INSN (insn));
-                   break;
-                 }
-               insn = NEXT_INSN (insn);
+               PUT_CODE (insn, NOTE);
+               NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+               NOTE_SOURCE_FILE (insn) = 0;
+             }
+           insn = basic_block_end[i];
+           if (GET_CODE (insn) != NOTE)
+             {
+               /* Turn the tail into a deleted insn note.  */
+               if (GET_CODE (insn) == BARRIER)
+                 abort ();
+               PUT_CODE (insn, NOTE);
+               NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+               NOTE_SOURCE_FILE (insn) = 0;
              }
+           /* BARRIERs are between basic blocks, not part of one.
+              Delete a BARRIER if the preceding jump is deleted.
+              We cannot alter a BARRIER into a NOTE
+              because it is too short; but we can really delete
+              it because it is not part of a basic block.  */
+           if (NEXT_INSN (insn) != 0
+               && GET_CODE (NEXT_INSN (insn)) == BARRIER)
+             delete_insn (NEXT_INSN (insn));
+
            /* Each time we delete some basic blocks,
               see if there is a jump around them that is
               being turned into a no-op.  If so, delete it.  */
@@ -569,7 +712,7 @@ find_basic_blocks (f)
            if (block_live[i - 1])
              {
                register int j;
-               for (j = i; j < n_basic_blocks; j++)
+               for (j = i + 1; j < n_basic_blocks; j++)
                  if (block_live[j])
                    {
                      rtx label;
@@ -594,9 +737,88 @@ find_basic_blocks (f)
                    }
              }
          }
+
+      /* There are pathological cases where one function calling hundreds of
+        nested inline functions can generate lots and lots of unreachable
+        blocks that jump can't delete.  Since we don't use sparse matrices
+        a lot of memory will be needed to compile such functions.
+        Implementing sparse matrices is a fair bit of work and it is not
+        clear that they win more than they lose (we don't want to
+        unnecessarily slow down compilation of normal code).  By making
+        another pass for the pathological case, we can greatly speed up
+        their compilation without hurting normal code.  This works because
+        all the insns in the unreachable blocks have either been deleted or
+        turned into notes.
+        Note that we're talking about reducing memory usage by 10's of
+        megabytes and reducing compilation time by several minutes.  */
+      /* ??? The choice of when to make another pass is a bit arbitrary,
+        and was derived from empirical data.  */
+      if (pass == 1
+         && deleted > 200)
+       {
+         pass++;
+         n_basic_blocks -= deleted;
+         /* `n_basic_blocks' may not be correct at this point: two previously
+            separate blocks may now be merged.  That's ok though as we
+            recalculate it during the second pass.  It certainly can't be
+            any larger than the current value.  */
+         goto restart;
+       }
     }
 }
 \f
+/* Subroutines of find_basic_blocks.  */
+
+/* Return 1 if X, the SRC_SRC of  SET of (pc) contain a REG or MEM that is
+   not in the constant pool and not in the condition of an IF_THEN_ELSE.  */
+
+static int
+jmp_uses_reg_or_mem (x)
+     rtx x;
+{
+  enum rtx_code code = GET_CODE (x);
+  int i, j;
+  char *fmt;
+
+  switch (code)
+    {
+    case CONST:
+    case LABEL_REF:
+    case PC:
+      return 0;
+
+    case REG:
+      return 1;
+
+    case MEM:
+      return ! (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+               && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)));
+
+    case IF_THEN_ELSE:
+      return (jmp_uses_reg_or_mem (XEXP (x, 1))
+             || jmp_uses_reg_or_mem (XEXP (x, 2)));
+
+    case PLUS:  case MINUS:  case MULT:
+      return (jmp_uses_reg_or_mem (XEXP (x, 0))
+             || jmp_uses_reg_or_mem (XEXP (x, 1)));
+    }
+
+  fmt = GET_RTX_FORMAT (code);
+  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'e'
+         && jmp_uses_reg_or_mem (XEXP (x, i)))
+       return 1;
+
+      if (fmt[i] == 'E')
+       for (j = 0; j < XVECLEN (x, i); j++)
+         if (jmp_uses_reg_or_mem (XVECEXP (x, i, j)))
+           return 1;
+    }
+
+  return 0;
+}
+
 /* Check expression X for label references;
    if one is found, add INSN to the label's chain of references.
 
@@ -657,6 +879,20 @@ mark_label_ref (x, insn, checkdup)
        }
     }
 }
+
+/* Delete INSN by patching it out.
+   Return the next insn.  */
+
+static rtx
+flow_delete_insn (insn)
+     rtx insn;
+{
+  /* ??? For the moment we assume we don't have to watch for NULLs here
+     since the start/end of basic blocks aren't deleted like this.  */
+  NEXT_INSN (PREV_INSN (insn)) = NEXT_INSN (insn);
+  PREV_INSN (NEXT_INSN (insn)) = PREV_INSN (insn);
+  return NEXT_INSN (insn);
+}
 \f
 /* Determine which registers are live at the start of each
    basic block of the function whose first insn is F.
@@ -670,7 +906,6 @@ life_analysis (f, nregs)
      rtx f;
      int nregs;
 {
-  register regset tem;
   int first_pass;
   int changed;
   /* For each basic block, a bitmask of regs
@@ -704,33 +939,34 @@ life_analysis (f, nregs)
   allocate_for_life_analysis ();
 
   reg_next_use = (rtx *) alloca (nregs * sizeof (rtx));
-  bzero (reg_next_use, nregs * sizeof (rtx));
+  bzero ((char *) reg_next_use, nregs * sizeof (rtx));
 
   /* Set up several regset-vectors used internally within this function.
      Their meanings are documented above, with their declarations.  */
 
-  basic_block_live_at_end = (regset *) alloca (n_basic_blocks * sizeof (regset));
+  basic_block_live_at_end
+    = (regset *) alloca (n_basic_blocks * sizeof (regset));
+
   /* Don't use alloca since that leads to a crash rather than an error message
      if there isn't enough space.
      Don't use oballoc since we may need to allocate other things during
      this function on the temporary obstack.  */
-  tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes);
-  bzero (tem, n_basic_blocks * regset_bytes);
-  init_regset_vector (basic_block_live_at_end, tem, n_basic_blocks, regset_bytes);
+  init_regset_vector (basic_block_live_at_end, n_basic_blocks, regset_bytes,
+                     &flow_obstack);
 
-  basic_block_new_live_at_end = (regset *) alloca (n_basic_blocks * sizeof (regset));
-  tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes);
-  bzero (tem, n_basic_blocks * regset_bytes);
-  init_regset_vector (basic_block_new_live_at_end, tem, n_basic_blocks, regset_bytes);
+  basic_block_new_live_at_end
+    = (regset *) alloca (n_basic_blocks * sizeof (regset));
+  init_regset_vector (basic_block_new_live_at_end, n_basic_blocks, regset_bytes,
+                     &flow_obstack);
 
-  basic_block_significant = (regset *) alloca (n_basic_blocks * sizeof (regset));
-  tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes);
-  bzero (tem, n_basic_blocks * regset_bytes);
-  init_regset_vector (basic_block_significant, tem, n_basic_blocks, regset_bytes);
+  basic_block_significant
+    = (regset *) alloca (n_basic_blocks * sizeof (regset));
+  init_regset_vector (basic_block_significant, n_basic_blocks, regset_bytes,
+                     &flow_obstack);
 
   /* Record which insns refer to any volatile memory
      or for any reason can't be deleted just because they are dead stores.
-     Also, delete any insns that copy a register to itself. */
+     Also, delete any insns that copy a register to itself.  */
 
   for (insn = f; insn; insn = NEXT_INSN (insn))
     {
@@ -743,10 +979,27 @@ life_analysis (f, nregs)
          if (GET_CODE (PATTERN (insn)) == SET
              && GET_CODE (SET_DEST (PATTERN (insn))) == REG
              && GET_CODE (SET_SRC (PATTERN (insn))) == REG
-             && REGNO (SET_DEST (PATTERN (insn))) ==
-                       REGNO (SET_SRC (PATTERN (insn)))
+             && (REGNO (SET_DEST (PATTERN (insn)))
+                 == REGNO (SET_SRC (PATTERN (insn))))
+             /* Insns carrying these notes are useful later on.  */
+             && ! find_reg_note (insn, REG_EQUAL, NULL_RTX))
+           {
+             PUT_CODE (insn, NOTE);
+             NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+             NOTE_SOURCE_FILE (insn) = 0;
+           }
+         /* Delete (in effect) any obvious no-op moves.  */
+         else if (GET_CODE (PATTERN (insn)) == SET
+             && GET_CODE (SET_DEST (PATTERN (insn))) == SUBREG
+             && GET_CODE (SUBREG_REG (SET_DEST (PATTERN (insn)))) == REG
+             && GET_CODE (SET_SRC (PATTERN (insn))) == SUBREG
+             && GET_CODE (SUBREG_REG (SET_SRC (PATTERN (insn)))) == REG
+             && (REGNO (SUBREG_REG (SET_DEST (PATTERN (insn))))
+                 == REGNO (SUBREG_REG (SET_SRC (PATTERN (insn)))))
+             && SUBREG_WORD (SET_DEST (PATTERN (insn))) ==
+                             SUBREG_WORD (SET_SRC (PATTERN (insn)))
              /* Insns carrying these notes are useful later on.  */
-             && ! find_reg_note (insn, REG_EQUAL, 0))
+             && ! find_reg_note (insn, REG_EQUAL, NULL_RTX))
            {
              PUT_CODE (insn, NOTE);
              NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
@@ -773,7 +1026,7 @@ life_analysis (f, nregs)
                
              if (i == XVECLEN (PATTERN (insn), 0)
                  /* Insns carrying these notes are useful later on.  */
-                 && ! find_reg_note (insn, REG_EQUAL, 0))
+                 && ! find_reg_note (insn, REG_EQUAL, NULL_RTX))
                {
                  PUT_CODE (insn, NOTE);
                  NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
@@ -810,25 +1063,45 @@ life_analysis (f, nregs)
       {
        /* If exiting needs the right stack value,
           consider the stack pointer live at the end of the function.  */
-       basic_block_live_at_end[n_basic_blocks - 1]
-         [STACK_POINTER_REGNUM / REGSET_ELT_BITS]
-           |= 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS);
-       basic_block_new_live_at_end[n_basic_blocks - 1]
-         [STACK_POINTER_REGNUM / REGSET_ELT_BITS]
-           |= 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS);
+       SET_REGNO_REG_SET (basic_block_live_at_end[n_basic_blocks - 1],
+                          STACK_POINTER_REGNUM);
+       SET_REGNO_REG_SET (basic_block_new_live_at_end[n_basic_blocks - 1],
+                          STACK_POINTER_REGNUM);
       }
 
-  /* Mark all global registers as being live at the end of the function
-     since they may be referenced by our caller.  */
+  /* Mark the frame pointer is needed at the end of the function.  If
+     we end up eliminating it, it will be removed from the live list
+     of each basic block by reload.  */
+
+  if (n_basic_blocks > 0)
+    {
+      SET_REGNO_REG_SET (basic_block_live_at_end[n_basic_blocks - 1],
+                        FRAME_POINTER_REGNUM);
+      SET_REGNO_REG_SET (basic_block_new_live_at_end[n_basic_blocks - 1],
+                        FRAME_POINTER_REGNUM);
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+      /* If they are different, also mark the hard frame pointer as live */
+      SET_REGNO_REG_SET (basic_block_live_at_end[n_basic_blocks - 1],
+                        HARD_FRAME_POINTER_REGNUM);
+      SET_REGNO_REG_SET (basic_block_new_live_at_end[n_basic_blocks - 1],
+                        HARD_FRAME_POINTER_REGNUM);
+#endif      
+      }
+
+  /* Mark all global registers and all registers used by the epilogue
+     as being live at the end of the function since they may be
+     referenced by our caller.  */
 
   if (n_basic_blocks > 0)
     for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-      if (global_regs[i])
+      if (global_regs[i]
+#ifdef EPILOGUE_USES
+         || EPILOGUE_USES (i)
+#endif
+         )
        {
-         basic_block_live_at_end[n_basic_blocks - 1]
-           [i / REGSET_ELT_BITS] |= 1 << (i % REGSET_ELT_BITS);
-         basic_block_new_live_at_end[n_basic_blocks - 1]
-           [i / REGSET_ELT_BITS] |= 1 << (i % REGSET_ELT_BITS);
+         SET_REGNO_REG_SET (basic_block_live_at_end[n_basic_blocks - 1], i);
+         SET_REGNO_REG_SET (basic_block_new_live_at_end[n_basic_blocks - 1], i);
        }
 
   /* Propagate life info through the basic blocks
@@ -863,20 +1136,18 @@ life_analysis (f, nregs)
                 reg that is live at the end now but was not live there before
                 is one of the significant regs of this basic block).  */
 
-             for (j = 0; j < regset_size; j++)
-               {
-                 register int x = (basic_block_new_live_at_end[i][j]
-                                   & ~basic_block_live_at_end[i][j]);
-                 if (x)
-                   consider = 1;
-                 if (x & basic_block_significant[i][j])
-                   {
-                     must_rescan = 1;
-                     consider = 1;
-                     break;
-                   }
-               }
-
+             EXECUTE_IF_AND_COMPL_IN_REG_SET (basic_block_new_live_at_end[i],
+                                              basic_block_live_at_end[i],
+                                              0, j,
+                                              {
+                                                consider = 1;
+                                                if (REGNO_REG_SET_P (basic_block_significant[i], j))
+                                                  {
+                                                    must_rescan = 1;
+                                                    goto done;
+                                                  }
+                                              });
+           done:
              if (! consider)
                continue;
            }
@@ -890,42 +1161,39 @@ life_analysis (f, nregs)
              /* No complete rescan needed;
                 just record those variables newly known live at end
                 as live at start as well.  */
-             for (j = 0; j < regset_size; j++)
-               {
-                 register int x = basic_block_new_live_at_end[i][j]
-                       & ~basic_block_live_at_end[i][j];
-                 basic_block_live_at_start[i][j] |= x;
-                 basic_block_live_at_end[i][j] |= x;
-               }
+             IOR_AND_COMPL_REG_SET (basic_block_live_at_start[i],
+                                    basic_block_new_live_at_end[i],
+                                    basic_block_live_at_end[i]);
+
+             IOR_AND_COMPL_REG_SET (basic_block_live_at_end[i],
+                                    basic_block_new_live_at_end[i],
+                                    basic_block_live_at_end[i]);
            }
          else
            {
              /* Update the basic_block_live_at_start
                 by propagation backwards through the block.  */
-             bcopy (basic_block_new_live_at_end[i],
-                    basic_block_live_at_end[i], regset_bytes);
-             bcopy (basic_block_live_at_end[i],
-                    basic_block_live_at_start[i], regset_bytes);
+             COPY_REG_SET (basic_block_live_at_end[i],
+                           basic_block_new_live_at_end[i]);
+             COPY_REG_SET (basic_block_live_at_start[i],
+                           basic_block_live_at_end[i]);
              propagate_block (basic_block_live_at_start[i],
                               basic_block_head[i], basic_block_end[i], 0,
-                              first_pass ? basic_block_significant[i] : 0,
+                              first_pass ? basic_block_significant[i]
+                              : (regset) 0,
                               i);
            }
 
          {
            register rtx jump, head;
+
            /* Update the basic_block_new_live_at_end's of the block
               that falls through into this one (if any).  */
            head = basic_block_head[i];
-           jump = PREV_INSN (head);
            if (basic_block_drops_in[i])
-             {
-               register int from_block = BLOCK_NUM (jump);
-               register int j;
-               for (j = 0; j < regset_size; j++)
-                 basic_block_new_live_at_end[from_block][j]
-                   |= basic_block_live_at_start[i][j];
-             }
+             IOR_REG_SET (basic_block_new_live_at_end[i-1],
+                          basic_block_live_at_start[i]);
+
            /* Update the basic_block_new_live_at_end's of
               all the blocks that jump to this one.  */
            if (GET_CODE (head) == CODE_LABEL)
@@ -934,10 +1202,8 @@ life_analysis (f, nregs)
                   jump = LABEL_NEXTREF (jump))
                {
                  register int from_block = BLOCK_NUM (CONTAINING_INSN (jump));
-                 register int j;
-                 for (j = 0; j < regset_size; j++)
-                   basic_block_new_live_at_end[from_block][j]
-                     |= basic_block_live_at_start[i][j];
+                 IOR_REG_SET (basic_block_new_live_at_end[from_block],
+                              basic_block_live_at_start[i]);
                }
          }
 #ifdef USE_C_ALLOCA
@@ -953,10 +1219,11 @@ life_analysis (f, nregs)
      one basic block.  */
 
   if (n_basic_blocks > 0)
-    for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
-      if (basic_block_live_at_start[0][i / REGSET_ELT_BITS]
-         & (1 << (i % REGSET_ELT_BITS)))
-       reg_basic_block[i] = REG_BLOCK_GLOBAL;
+    EXECUTE_IF_SET_IN_REG_SET (basic_block_live_at_start[0],
+                              FIRST_PSEUDO_REGISTER, i,
+                              {
+                                REG_BASIC_BLOCK (i) = REG_BLOCK_GLOBAL;
+                              });
 
   /* Now the life information is accurate.
      Make one more pass over each basic block
@@ -973,7 +1240,8 @@ life_analysis (f, nregs)
   for (i = 0; i < n_basic_blocks; i++)
     {
       propagate_block (basic_block_live_at_end[i],
-                      basic_block_head[i], basic_block_end[i], 1, 0, i);
+                      basic_block_head[i], basic_block_end[i], 1,
+                      (regset) 0, i);
 #ifdef USE_C_ALLOCA
       alloca (0);
 #endif
@@ -986,13 +1254,16 @@ life_analysis (f, nregs)
      But we don't need to do this for the user's variables, since
      ANSI says only volatile variables need this.  */
 #ifdef LONGJMP_RESTORE_FROM_STACK
-  for (i = FIRST_PSEUDO_REGISTER; i < nregs; i++)
-    if (regs_live_at_setjmp[i / REGSET_ELT_BITS] & (1 << (i % REGSET_ELT_BITS))
-       && regno_reg_rtx[i] != 0 && ! REG_USERVAR_P (regno_reg_rtx[i]))
-      {
-       reg_live_length[i] = -1;
-       reg_basic_block[i] = -1;
-      }
+  EXECUTE_IF_SET_IN_REG_SET (regs_live_at_setjmp,
+                            FIRST_PSEUDO_REGISTER, i,
+                            {
+                              if (regno_reg_rtx[i] != 0
+                                  && ! REG_USERVAR_P (regno_reg_rtx[i]))
+                                {
+                                  REG_LIVE_LENGTH (i) = -1;
+                                  REG_BASIC_BLOCK (i) = -1;
+                                }
+                            });
 #endif
 #endif
 
@@ -1005,15 +1276,17 @@ life_analysis (f, nregs)
      If the pseudo goes in a hard reg, some other value may occupy
      that hard reg where this pseudo is dead, thus clobbering the pseudo.
      Conclusion: such a pseudo must not go in a hard reg.  */
-  for (i = FIRST_PSEUDO_REGISTER; i < nregs; i++)
-    if (regs_live_at_setjmp[i / REGSET_ELT_BITS] & (1 << (i % REGSET_ELT_BITS))
-       && regno_reg_rtx[i] != 0)
-      {
-       reg_live_length[i] = -1;
-       reg_basic_block[i] = -1;
-      }
-
-  obstack_free (&flow_obstack, 0);
+  EXECUTE_IF_SET_IN_REG_SET (regs_live_at_setjmp,
+                            FIRST_PSEUDO_REGISTER, i,
+                            {
+                              if (regno_reg_rtx[i] != 0)
+                                {
+                                  REG_LIVE_LENGTH (i) = -1;
+                                  REG_BASIC_BLOCK (i) = -1;
+                                }
+                            });
+
+  obstack_free (&flow_obstack, NULL_PTR);
 }
 \f
 /* Subroutines of life analysis.  */
@@ -1025,37 +1298,26 @@ void
 allocate_for_life_analysis ()
 {
   register int i;
-  register regset tem;
 
   regset_size = ((max_regno + REGSET_ELT_BITS - 1) / REGSET_ELT_BITS);
-  regset_bytes = regset_size * sizeof (*(regset)0);
-
-  reg_n_refs = (int *) oballoc (max_regno * sizeof (int));
-  bzero (reg_n_refs, max_regno * sizeof (int));
-
-  reg_n_sets = (short *) oballoc (max_regno * sizeof (short));
-  bzero (reg_n_sets, max_regno * sizeof (short));
-
-  reg_n_deaths = (short *) oballoc (max_regno * sizeof (short));
-  bzero (reg_n_deaths, max_regno * sizeof (short));
+  regset_bytes = regset_size * sizeof (*(regset) 0);
 
-  reg_live_length = (int *) oballoc (max_regno * sizeof (int));
-  bzero (reg_live_length, max_regno * sizeof (int));
+  /* Because both reg_scan and flow_analysis want to set up the REG_N_SETS
+     information, explicitly reset it here.  The allocation should have
+     already happened on the previous reg_scan pass.  Make sure in case
+     some more registers were allocated.  */
+  allocate_reg_info (max_regno, FALSE, FALSE);
 
-  reg_n_calls_crossed = (int *) oballoc (max_regno * sizeof (int));
-  bzero (reg_n_calls_crossed, max_regno * sizeof (int));
-
-  reg_basic_block = (short *) oballoc (max_regno * sizeof (short));
   for (i = 0; i < max_regno; i++)
-    reg_basic_block[i] = REG_BLOCK_UNKNOWN;
+    REG_N_SETS (i) = 0;
 
-  basic_block_live_at_start = (regset *) oballoc (n_basic_blocks * sizeof (regset));
-  tem = (regset) oballoc (n_basic_blocks * regset_bytes);
-  bzero (tem, n_basic_blocks * regset_bytes);
-  init_regset_vector (basic_block_live_at_start, tem, n_basic_blocks, regset_bytes);
+  basic_block_live_at_start
+    = (regset *) oballoc (n_basic_blocks * sizeof (regset));
+  init_regset_vector (basic_block_live_at_start, n_basic_blocks, regset_bytes,
+                     function_obstack);
 
-  regs_live_at_setjmp = (regset) oballoc (regset_bytes);
-  bzero (regs_live_at_setjmp, regset_bytes);
+  regs_live_at_setjmp = OBSTACK_ALLOC_REG_SET (function_obstack);
+  CLEAR_REG_SET (regs_live_at_setjmp);
 }
 
 /* Make each element of VECTOR point at a regset,
@@ -1064,22 +1326,21 @@ allocate_for_life_analysis ()
    BYTES_PER_ELT is the number of bytes in one regset.  */
 
 static void
-init_regset_vector (vector, space, nelts, bytes_per_elt)
+init_regset_vector (vector, nelts, bytes_per_elt, alloc_obstack)
      regset *vector;
-     regset space;
      int nelts;
      int bytes_per_elt;
+     struct obstack *alloc_obstack;
 {
   register int i;
-  register regset p = space;
 
   for (i = 0; i < nelts; i++)
     {
-      vector[i] = p;
-      p += bytes_per_elt / sizeof (*p);
+      vector[i] = OBSTACK_ALLOC_REG_SET (alloc_obstack);
+      CLEAR_REG_SET (vector[i]);
     }
 }
-\f
+
 /* Compute the registers live at the beginning of a basic block
    from those live at the end.
 
@@ -1116,11 +1377,8 @@ propagate_block (old, first, last, final, significant, bnum)
   /* The following variables are used only if FINAL is nonzero.  */
   /* This vector gets one element for each reg that has been live
      at any point in the basic block that has been scanned so far.
-     SOMETIMES_MAX says how many elements are in use so far.
-     In each element, OFFSET is the byte-number within a regset
-     for the register described by the element, and BIT is a mask
-     for that register's bit within the byte.  */
-  register struct foo { short offset; short bit; } *regs_sometimes_live;
+     SOMETIMES_MAX says how many elements are in use so far.  */
+  register int *regs_sometimes_live;
   int sometimes_max = 0;
   /* This regset has 1 for each reg that we have seen live so far.
      It and REGS_SOMETIMES_LIVE are updated together.  */
@@ -1131,8 +1389,8 @@ propagate_block (old, first, last, final, significant, bnum)
      current basic block, and adjust as we pass ends and starts of loops.  */
   loop_depth = basic_block_loop_depth[bnum];
 
-  dead = (regset) alloca (regset_bytes);
-  live = (regset) alloca (regset_bytes);
+  dead = ALLOCA_REG_SET ();
+  live = ALLOCA_REG_SET ();
 
   cc0_live = 0;
   last_mem_set = 0;
@@ -1152,31 +1410,22 @@ propagate_block (old, first, last, final, significant, bnum)
 
   if (final)
     {
-      register int i, offset, bit;
+      register int i;
 
       num_scratch = 0;
-      maxlive = (regset) alloca (regset_bytes);
-      bcopy (old, maxlive, regset_bytes);
-      regs_sometimes_live
-       = (struct foo *) alloca (max_regno * sizeof (struct foo));
+      maxlive = ALLOCA_REG_SET ();
+      COPY_REG_SET (maxlive, old);
+      regs_sometimes_live = (int *) alloca (max_regno * sizeof (int));
 
       /* Process the regs live at the end of the block.
         Enter them in MAXLIVE and REGS_SOMETIMES_LIVE.
-        Also mark them as not local to any one basic block.  */
-
-      for (offset = 0, i = 0; offset < regset_size; offset++)
-       for (bit = 1; bit; bit <<= 1, i++)
-         {
-           if (i == max_regno)
-             break;
-           if (old[offset] & bit)
-             {
-               reg_basic_block[i] = REG_BLOCK_GLOBAL;
-               regs_sometimes_live[sometimes_max].offset = offset;
-               regs_sometimes_live[sometimes_max].bit = i % REGSET_ELT_BITS;
-               sometimes_max++;
-             }
-         }
+        Also mark them as not local to any one basic block. */
+      EXECUTE_IF_SET_IN_REG_SET (old, 0, i,
+                                {
+                                  REG_BASIC_BLOCK (i) = REG_BLOCK_GLOBAL;
+                                  regs_sometimes_live[sometimes_max] = i;
+                                  sometimes_max++;
+                                });
     }
 
   /* Scan the block an insn at a time from end to beginning.  */
@@ -1185,28 +1434,25 @@ propagate_block (old, first, last, final, significant, bnum)
     {
       prev = PREV_INSN (insn);
 
-      /* Look for loop boundaries, remembering that we are going backwards.  */
-      if (GET_CODE (insn) == NOTE
-         && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
-       loop_depth++;
-      else if (GET_CODE (insn) == NOTE
-              && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
-       loop_depth--;
-
-      /* If we have LOOP_DEPTH == 0, there has been a bookkeeping error. 
-        Abort now rather than setting register status incorrectly.  */
-      if (loop_depth == 0)
-       abort ();
-
-      /* If this is a call to `setjmp' et al,
-        warn if any non-volatile datum is live.  */
-
-      if (final && GET_CODE (insn) == NOTE
-         && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
+      if (GET_CODE (insn) == NOTE)
        {
-         int i;
-         for (i = 0; i < regset_size; i++)
-           regs_live_at_setjmp[i] |= old[i];
+         /* Look for loop boundaries, remembering that we are going
+            backwards.  */
+         if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
+           loop_depth++;
+         else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
+           loop_depth--;
+
+         /* If we have LOOP_DEPTH == 0, there has been a bookkeeping error. 
+            Abort now rather than setting register status incorrectly.  */
+         if (loop_depth == 0)
+           abort ();
+
+         /* If this is a call to `setjmp' et al,
+            warn if any non-volatile datum is live.  */
+
+         if (final && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
+           IOR_REG_SET (regs_live_at_setjmp, old);
        }
 
       /* Update the life-status of regs for this insn.
@@ -1216,10 +1462,10 @@ propagate_block (old, first, last, final, significant, bnum)
         are those live after, with DEAD regs turned off,
         and then LIVE regs turned on.  */
 
-      if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+      else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
        {
          register int i;
-         rtx note = find_reg_note (insn, REG_RETVAL, 0);
+         rtx note = find_reg_note (insn, REG_RETVAL, NULL_RTX);
          int insn_is_dead
            = (insn_dead_p (PATTERN (insn), old, 0)
               /* Don't delete something that refers to volatile storage!  */
@@ -1238,6 +1484,11 @@ propagate_block (old, first, last, final, significant, bnum)
              NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
              NOTE_SOURCE_FILE (insn) = 0;
 
+             /* CC0 is now known to be dead.  Either this insn used it,
+                in which case it doesn't anymore, or clobbered it,
+                so the next insn can't use it.  */
+             cc0_live = 0;
+
              /* If this insn is copying the return value from a library call,
                 delete the entire library call.  */
              if (libcall_is_dead)
@@ -1257,11 +1508,8 @@ propagate_block (old, first, last, final, significant, bnum)
              goto flushed;
            }
 
-         for (i = 0; i < regset_size; i++)
-           {
-             dead[i] = 0;      /* Faster than bzero here */
-             live[i] = 0;      /* since regset_size is usually small */
-           }
+         CLEAR_REG_SET (dead);
+         CLEAR_REG_SET (live);
 
          /* See if this is an increment or decrement that can be
             merged into a following memory address.  */
@@ -1291,7 +1539,7 @@ propagate_block (old, first, last, final, significant, bnum)
          if (libcall_is_dead)
            {
              /* Mark the dest reg as `significant'.  */
-             mark_set_regs (old, dead, PATTERN (insn), 0, significant);
+             mark_set_regs (old, dead, PATTERN (insn), NULL_RTX, significant);
 
              insn = XEXP (note, 0);
              prev = PREV_INSN (insn);
@@ -1312,8 +1560,8 @@ propagate_block (old, first, last, final, significant, bnum)
                 DEAD gets those set by it.  Dead insns don't make anything
                 live.  */
 
-             mark_set_regs (old, dead, PATTERN (insn), final ? insn : 0,
-                            significant);
+             mark_set_regs (old, dead, PATTERN (insn),
+                            final ? insn : NULL_RTX, significant);
 
              /* If an insn doesn't use CC0, it becomes dead since we 
                 assume that every insn clobbers it.  So show it dead here;
@@ -1334,38 +1582,43 @@ propagate_block (old, first, last, final, significant, bnum)
                {
                  register int i;
 
+                 rtx note;
+
+                 for (note = CALL_INSN_FUNCTION_USAGE (insn);
+                      note;
+                      note = XEXP (note, 1))
+                   if (GET_CODE (XEXP (note, 0)) == USE)
+                     mark_used_regs (old, live, SET_DEST (XEXP (note, 0)),
+                                     final, insn);
+
                  /* Each call clobbers all call-clobbered regs that are not
-                    global.  Note that the function-value reg is a
+                    global or fixed.  Note that the function-value reg is a
                     call-clobbered reg, and mark_set_regs has already had
                     a chance to handle it.  */
 
                  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-                   if (call_used_regs[i] && ! global_regs[i])
-                     dead[i / REGSET_ELT_BITS]
-                       |= (1 << (i % REGSET_ELT_BITS));
+                   if (call_used_regs[i] && ! global_regs[i]
+                       && ! fixed_regs[i])
+                     SET_REGNO_REG_SET (dead, i);
 
                  /* The stack ptr is used (honorarily) by a CALL insn.  */
-                 live[STACK_POINTER_REGNUM / REGSET_ELT_BITS]
-                   |= (1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS));
+                 SET_REGNO_REG_SET (live, STACK_POINTER_REGNUM);
 
                  /* Calls may also reference any of the global registers,
                     so they are made live.  */
-
                  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
                    if (global_regs[i])
-                     live[i / REGSET_ELT_BITS]
-                       |= (1 << (i % REGSET_ELT_BITS));
+                     mark_used_regs (old, live,
+                                     gen_rtx (REG, reg_raw_mode[i], i),
+                                     final, insn);
 
                  /* Calls also clobber memory.  */
                  last_mem_set = 0;
                }
 
              /* Update OLD for the registers used or set.  */
-             for (i = 0; i < regset_size; i++)
-               {
-                 old[i] &= ~dead[i];
-                 old[i] |= live[i];
-               }
+             AND_COMPL_REG_SET (old, dead);
+             IOR_REG_SET (old, live);
 
              if (GET_CODE (insn) == CALL_INSN && final)
                {
@@ -1373,11 +1626,11 @@ propagate_block (old, first, last, final, significant, bnum)
                     must not go in a register clobbered by calls.
                     Find all regs now live and record this for them.  */
 
-                 register struct foo *p = regs_sometimes_live;
+                 register int *p = regs_sometimes_live;
 
                  for (i = 0; i < sometimes_max; i++, p++)
-                   if (old[p->offset] & (1 << p->bit))
-                     reg_n_calls_crossed[p->offset * REGSET_ELT_BITS + p->bit]+= 1;
+                   if (REGNO_REG_SET_P (old, *p))
+                     REG_N_CALLS_CROSSED (*p)++;
                }
            }
 
@@ -1387,33 +1640,22 @@ propagate_block (old, first, last, final, significant, bnum)
 
          if (final)
            {
-             for (i = 0; i < regset_size; i++)
-               {
-                 register int diff = live[i] & ~maxlive[i];
+             register int regno;
+             register int *p;
 
-                 if (diff)
-                   {
-                     register int regno;
-                     maxlive[i] |= diff;
-                     for (regno = 0; diff && regno < REGSET_ELT_BITS; regno++)
-                       if (diff & (1 << regno))
-                         {
-                           regs_sometimes_live[sometimes_max].offset = i;
-                           regs_sometimes_live[sometimes_max].bit = regno;
-                           diff &= ~ (1 << regno);
-                           sometimes_max++;
-                         }
-                   }
-               }
+             EXECUTE_IF_AND_COMPL_IN_REG_SET (live, maxlive, 0, regno,
+                                              {
+                                                regs_sometimes_live[sometimes_max++] = regno;
+                                                SET_REGNO_REG_SET (maxlive, regno);
+                                              });
 
-             {
-               register struct foo *p = regs_sometimes_live;
-               for (i = 0; i < sometimes_max; i++, p++)
-                 {
-                   if (old[p->offset] & (1 << p->bit))
-                     reg_live_length[p->offset * REGSET_ELT_BITS + p->bit]++;
-                 }
-             }
+             p = regs_sometimes_live;
+             for (i = 0; i < sometimes_max; i++)
+               {
+                 regno = *p++;
+                 if (REGNO_REG_SET_P (old, regno))
+                   REG_LIVE_LENGTH (regno)++;
+               }
            }
        }
     flushed: ;
@@ -1466,15 +1708,21 @@ insn_dead_p (x, needed, call_ok)
       if (GET_CODE (r) == REG)
        {
          register int regno = REGNO (r);
-         register int offset = regno / REGSET_ELT_BITS;
-         register int bit = 1 << (regno % REGSET_ELT_BITS);
 
+         /* Don't delete insns to set global regs.  */
          if ((regno < FIRST_PSEUDO_REGISTER && global_regs[regno])
              /* Make sure insns to set frame pointer aren't deleted.  */
              || regno == FRAME_POINTER_REGNUM
-             /* Make sure insns to set arg pointer are never deleted.  */
-             || regno == ARG_POINTER_REGNUM
-             || (needed[offset] & bit) != 0)
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+             || regno == HARD_FRAME_POINTER_REGNUM
+#endif
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+             /* Make sure insns to set arg pointer are never deleted
+                (if the arg pointer isn't fixed, there will be a USE for
+                it, so we can treat it normally).  */
+             || (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
+#endif
+             || REGNO_REG_SET_P (needed, regno))
            return 0;
 
          /* If this is a hard register, verify that subsequent words are
@@ -1484,8 +1732,7 @@ insn_dead_p (x, needed, call_ok)
              int n = HARD_REGNO_NREGS (regno, GET_MODE (r));
 
              while (--n > 0)
-               if ((needed[(regno + n) / REGSET_ELT_BITS]
-                    & 1 << ((regno + n) % REGSET_ELT_BITS)) != 0)
+               if (REGNO_REG_SET_P (needed, regno+n))
                  return 0;
            }
 
@@ -1565,8 +1812,11 @@ libcall_dead_p (x, needed, note, insn)
                    && GET_CODE (SET_SRC (XVECEXP (call, 0, i))) == CALL)
                  break;
 
+             /* This may be a library call that is returning a value
+                via invisible pointer.  Do nothing special, since
+                ordinary death handling can understand these insns.  */
              if (i < 0)
-               abort ();
+               return 0;
 
              call = XVECEXP (call, 0, i);
            }
@@ -1578,17 +1828,20 @@ libcall_dead_p (x, needed, note, insn)
 }
 
 /* Return 1 if register REGNO was used before it was set.
-   In other words, if it is live at function entry.  */
+   In other words, if it is live at function entry.
+   Don't count global register variables or variables in registers
+   that can be used for function arg passing, though.  */
 
 int
 regno_uninitialized (regno)
      int regno;
 {
-  if (n_basic_blocks == 0)
+  if (n_basic_blocks == 0
+      || (regno < FIRST_PSEUDO_REGISTER
+         && (global_regs[regno] || FUNCTION_ARG_REGNO_P (regno))))
     return 0;
 
-  return (basic_block_live_at_start[0][regno / REGSET_ELT_BITS]
-         & (1 << (regno % REGSET_ELT_BITS)));
+  return REGNO_REG_SET_P (basic_block_live_at_start[0], regno);
 }
 
 /* 1 if register REGNO was alive at a place where `setjmp' was called
@@ -1602,11 +1855,9 @@ regno_clobbered_at_setjmp (regno)
   if (n_basic_blocks == 0)
     return 0;
 
-  return ((reg_n_sets[regno] > 1
-          || (basic_block_live_at_start[0][regno / REGSET_ELT_BITS]
-              & (1 << (regno % REGSET_ELT_BITS))))
-         && (regs_live_at_setjmp[regno / REGSET_ELT_BITS]
-             & (1 << (regno % REGSET_ELT_BITS))));
+  return ((REG_N_SETS (regno) > 1
+          || REGNO_REG_SET_P (basic_block_live_at_start[0], regno))
+         && REGNO_REG_SET_P (regs_live_at_setjmp, regno));
 }
 \f
 /* Process the registers that are set within X.
@@ -1618,8 +1869,6 @@ regno_clobbered_at_setjmp (regno)
    in propagate_block.  In this case, various info about register
    usage is stored, LOG_LINKS fields of insns are set up.  */
 
-static void mark_set_1 ();
-
 static void
 mark_set_regs (needed, dead, x, insn, significant)
      regset needed;
@@ -1692,21 +1941,24 @@ mark_set_1 (needed, dead, x, insn, significant)
 
   if (GET_CODE (reg) == REG
       && (regno = REGNO (reg), regno != FRAME_POINTER_REGNUM)
-      && regno != ARG_POINTER_REGNUM
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+      && regno != HARD_FRAME_POINTER_REGNUM
+#endif
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+      && ! (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
+#endif
       && ! (regno < FIRST_PSEUDO_REGISTER && global_regs[regno]))
     /* && regno != STACK_POINTER_REGNUM) -- let's try without this.  */
     {
-      register int offset = regno / REGSET_ELT_BITS;
-      register int bit = 1 << (regno % REGSET_ELT_BITS);
-      int all_needed = (needed[offset] & bit) != 0;
-      int some_needed = (needed[offset] & bit) != 0;
+      int some_needed = REGNO_REG_SET_P (needed, regno);
+      int some_not_needed = ! some_needed;
 
       /* Mark it as a significant register for this basic block.  */
       if (significant)
-       significant[offset] |= bit;
+       SET_REGNO_REG_SET (significant, regno);
 
       /* Mark it as as dead before this insn.  */
-      dead[offset] |= bit;
+      SET_REGNO_REG_SET (dead, regno);
 
       /* A hard reg in a wide mode may really be multiple registers.
         If so, mark all of them just like the first.  */
@@ -1722,15 +1974,14 @@ mark_set_1 (needed, dead, x, insn, significant)
          n = HARD_REGNO_NREGS (regno, GET_MODE (reg));
          while (--n > 0)
            {
+             int regno_n = regno + n;
+             int needed_regno = REGNO_REG_SET_P (needed, regno_n);
              if (significant)
-               significant[(regno + n) / REGSET_ELT_BITS]
-                 |= 1 << ((regno + n) % REGSET_ELT_BITS);
-             dead[(regno + n) / REGSET_ELT_BITS]
-               |= 1 << ((regno + n) % REGSET_ELT_BITS);
-             some_needed |= (needed[(regno + n) / REGSET_ELT_BITS]
-                             & 1 << ((regno + n) % REGSET_ELT_BITS));
-             all_needed &= (needed[(regno + n) / REGSET_ELT_BITS]
-                            & 1 << ((regno + n) % REGSET_ELT_BITS));
+               SET_REGNO_REG_SET (significant, regno_n);
+
+             SET_REGNO_REG_SET (dead, regno_n);
+             some_needed |= needed_regno;
+             some_not_needed |= ! needed_regno;
            }
        }
       /* Additional data to record if this is the final pass.  */
@@ -1748,36 +1999,41 @@ mark_set_1 (needed, dead, x, insn, significant)
 
              for (i = regno; i < endregno; i++)
                {
+                 /* The next use is no longer "next", since a store
+                    intervenes.  */
+                 reg_next_use[i] = 0;
+
                  regs_ever_live[i] = 1;
-                 reg_n_sets[i]++;
+                 REG_N_SETS (i)++;
                }
            }
          else
            {
+             /* The next use is no longer "next", since a store
+                intervenes.  */
+             reg_next_use[regno] = 0;
+
              /* Keep track of which basic blocks each reg appears in.  */
 
-             if (reg_basic_block[regno] == REG_BLOCK_UNKNOWN)
-               reg_basic_block[regno] = blocknum;
-             else if (reg_basic_block[regno] != blocknum)
-               reg_basic_block[regno] = REG_BLOCK_GLOBAL;
+             if (REG_BASIC_BLOCK (regno) == REG_BLOCK_UNKNOWN)
+               REG_BASIC_BLOCK (regno) = blocknum;
+             else if (REG_BASIC_BLOCK (regno) != blocknum)
+               REG_BASIC_BLOCK (regno) = REG_BLOCK_GLOBAL;
 
              /* Count (weighted) references, stores, etc.  This counts a
                 register twice if it is modified, but that is correct.  */
-             reg_n_sets[regno]++;
+             REG_N_SETS (regno)++;
 
-             reg_n_refs[regno] += loop_depth;
+             REG_N_REFS (regno) += loop_depth;
                  
              /* The insns where a reg is live are normally counted
                 elsewhere, but we want the count to include the insn
                 where the reg is set, and the normal counting mechanism
                 would not count it.  */
-             reg_live_length[regno]++;
+             REG_LIVE_LENGTH (regno)++;
            }
 
-         /* The next use is no longer "next", since a store intervenes.  */
-         reg_next_use[regno] = 0;
-
-         if (all_needed)
+         if (! some_not_needed)
            {
              /* Make a logical link from the next following insn
                 that uses this register, back to this insn.
@@ -1802,7 +2058,7 @@ mark_set_1 (needed, dead, x, insn, significant)
                 Indicate this by marking the reg being set as dying here.  */
              REG_NOTES (insn)
                = gen_rtx (EXPR_LIST, REG_UNUSED, reg, REG_NOTES (insn));
-             reg_n_deaths[REGNO (reg)]++;
+             REG_N_DEATHS (REGNO (reg))++;
            }
          else
            {
@@ -1816,15 +2072,17 @@ mark_set_1 (needed, dead, x, insn, significant)
 
              for (i = HARD_REGNO_NREGS (regno, GET_MODE (reg)) - 1;
                   i >= 0; i--)
-               if ((needed[(regno + i) / REGSET_ELT_BITS]
-                    & 1 << ((regno + i) % REGSET_ELT_BITS)) == 0)
+               if (!REGNO_REG_SET_P (needed, regno + i))
                  REG_NOTES (insn)
                    = gen_rtx (EXPR_LIST, REG_UNUSED,
-                              gen_rtx (REG, word_mode, regno + i),
+                              gen_rtx (REG, reg_raw_mode[regno + i],
+                                       regno + i),
                               REG_NOTES (insn));
            }
        }
     }
+  else if (GET_CODE (reg) == REG)
+    reg_next_use[regno] = 0;
 
   /* If this is the last pass and this is a SCRATCH, show it will be dying
      here and count it.  */
@@ -1848,7 +2106,8 @@ find_auto_inc (needed, x, insn)
      rtx insn;
 {
   rtx addr = XEXP (x, 0);
-  int offset = 0;
+  HOST_WIDE_INT offset = 0;
+  rtx set;
 
   /* Here we detect use of an index register which might be good for
      postincrement, postdecrement, preincrement, or predecrement.  */
@@ -1865,13 +2124,14 @@ find_auto_inc (needed, x, insn)
       int regno = REGNO (addr);
 
       /* Is the next use an increment that might make auto-increment? */
-      incr = reg_next_use[regno];
-      if (incr && GET_CODE (PATTERN (incr)) == SET
+      if ((incr = reg_next_use[regno]) != 0
+         && (set = single_set (incr)) != 0
+         && GET_CODE (set) == SET
          && BLOCK_NUM (incr) == BLOCK_NUM (insn)
          /* Can't add side effects to jumps; if reg is spilled and
             reloaded, there's no way to store back the altered value.  */
          && GET_CODE (insn) != JUMP_INSN
-         && (y = SET_SRC (PATTERN (incr)), GET_CODE (y) == PLUS)
+         && (y = SET_SRC (set), GET_CODE (y) == PLUS)
          && XEXP (y, 0) == addr
          && GET_CODE (XEXP (y, 1)) == CONST_INT
          && (0
@@ -1892,16 +2152,33 @@ find_auto_inc (needed, x, insn)
          && (use = find_use_as_address (PATTERN (insn), addr, offset),
              use != 0 && use != (rtx) 1))
        {
-         int win = 0;
-         rtx q = SET_DEST (PATTERN (incr));
+         rtx q = SET_DEST (set);
+         enum rtx_code inc_code = (INTVAL (XEXP (y, 1)) == size
+                                   ? (offset ? PRE_INC : POST_INC)
+                                   : (offset ? PRE_DEC : POST_DEC));
 
          if (dead_or_set_p (incr, addr))
-           win = 1;
-         else if (GET_CODE (q) == REG && ! reg_used_between_p (q, insn, incr))
            {
-             /* We have *p followed by q = p+size.
+             /* This is the simple case.  Try to make the auto-inc.  If
+                we can't, we are done.  Otherwise, we will do any
+                needed updates below.  */
+             if (! validate_change (insn, &XEXP (x, 0),
+                                    gen_rtx (inc_code, Pmode, addr),
+                                    0))
+               return;
+           }
+         else if (GET_CODE (q) == REG
+                  /* PREV_INSN used here to check the semi-open interval
+                     [insn,incr).  */
+                  && ! reg_used_between_p (q,  PREV_INSN (insn), incr)
+                  /* We must also check for sets of q as q may be
+                     a call clobbered hard register and there may
+                     be a call between PREV_INSN (insn) and incr.  */
+                  && ! reg_set_between_p (q,  PREV_INSN (insn), incr))
+           {
+             /* We have *p followed sometime later by q = p+size.
                 Both p and q must be live afterward,
-                and q must be dead before.
+                and q is not used between INSN and it's assignment.
                 Change it to q = p, ...*q..., q = q+size.
                 Then fall into the usual case.  */
              rtx insns, temp;
@@ -1921,14 +2198,25 @@ find_auto_inc (needed, x, insn)
                  BLOCK_NUM (temp) = BLOCK_NUM (insn);
                }
 
+             /* If we can't make the auto-inc, or can't make the
+                replacement into Y, exit.  There's no point in making
+                the change below if we can't do the auto-inc and doing
+                so is not correct in the pre-inc case.  */
+
+             validate_change (insn, &XEXP (x, 0),
+                              gen_rtx (inc_code, Pmode, q),
+                              1);
+             validate_change (incr, &XEXP (y, 0), q, 1);
+             if (! apply_change_group ())
+               return;
+
+             /* We now know we'll be doing this change, so emit the
+                new insn(s) and do the updates.  */
              emit_insns_before (insns, insn);
 
              if (basic_block_head[BLOCK_NUM (insn)] == insn)
                basic_block_head[BLOCK_NUM (insn)] = insns;
 
-             XEXP (x, 0) = q;
-             XEXP (y, 0) = q;
-
              /* INCR will become a NOTE and INSN won't contain a
                 use of ADDR.  If a use of ADDR was just placed in
                 the insn before INSN, make that the next use. 
@@ -1942,62 +2230,54 @@ find_auto_inc (needed, x, insn)
 
              addr = q;
              regno = REGNO (q);
-             win = 1;
 
              /* REGNO is now used in INCR which is below INSN, but
                 it previously wasn't live here.  If we don't mark
                 it as needed, we'll put a REG_DEAD note for it
                 on this insn, which is incorrect.  */
-             needed[regno / REGSET_ELT_BITS]
-               |= 1 << (regno % REGSET_ELT_BITS);
+             SET_REGNO_REG_SET (needed, regno);
 
              /* If there are any calls between INSN and INCR, show
                 that REGNO now crosses them.  */
              for (temp = insn; temp != incr; temp = NEXT_INSN (temp))
                if (GET_CODE (temp) == CALL_INSN)
-                 reg_n_calls_crossed[regno]++;
+                 REG_N_CALLS_CROSSED (regno)++;
            }
+         else
+           return;
 
-         if (win)
+         /* If we haven't returned, it means we were able to make the
+            auto-inc, so update the status.  First, record that this insn
+            has an implicit side effect.  */
+
+         REG_NOTES (insn)
+           = gen_rtx (EXPR_LIST, REG_INC, addr, REG_NOTES (insn));
+
+         /* Modify the old increment-insn to simply copy
+            the already-incremented value of our register.  */
+         if (! validate_change (incr, &SET_SRC (set), addr, 0))
+           abort ();
+
+         /* If that makes it a no-op (copying the register into itself) delete
+            it so it won't appear to be a "use" and a "set" of this
+            register.  */
+         if (SET_DEST (set) == addr)
            {
-             /* We have found a suitable auto-increment: do POST_INC around
-                the register here, and patch out the increment instruction 
-                that follows. */
-             XEXP (x, 0) = gen_rtx ((INTVAL (XEXP (y, 1)) == size
-                                     ? (offset ? PRE_INC : POST_INC)
-                                     : (offset ? PRE_DEC : POST_DEC)),
-                                    Pmode, addr);
-
-             /* Record that this insn has an implicit side effect.  */
-             REG_NOTES (insn)
-               = gen_rtx (EXPR_LIST, REG_INC, addr, REG_NOTES (insn));
-
-             /* Modify the old increment-insn to simply copy
-                the already-incremented value of our register.  */
-             SET_SRC (PATTERN (incr)) = addr;
-             /* Indicate insn must be re-recognized.  */
-             INSN_CODE (incr) = -1;
-
-             /* If that makes it a no-op (copying the register into itself)
-                then delete it so it won't appear to be a "use" and a "set"
-                of this register.  */
-             if (SET_DEST (PATTERN (incr)) == addr)
-               {
-                 PUT_CODE (incr, NOTE);
-                 NOTE_LINE_NUMBER (incr) = NOTE_INSN_DELETED;
-                 NOTE_SOURCE_FILE (incr) = 0;
-               }
+             PUT_CODE (incr, NOTE);
+             NOTE_LINE_NUMBER (incr) = NOTE_INSN_DELETED;
+             NOTE_SOURCE_FILE (incr) = 0;
+           }
 
-             if (regno >= FIRST_PSEUDO_REGISTER)
-               {
-                 /* Count an extra reference to the reg.  When a reg is
-                    incremented, spilling it is worse, so we want to make
-                    that less likely.  */
-                 reg_n_refs[regno] += loop_depth;
-                 /* Count the increment as a setting of the register,
-                    even though it isn't a SET in rtl.  */
-                 reg_n_sets[regno]++;
-               }
+         if (regno >= FIRST_PSEUDO_REGISTER)
+           {
+             /* Count an extra reference to the reg.  When a reg is
+                incremented, spilling it is worse, so we want to make
+                that less likely.  */
+             REG_N_REFS (regno) += loop_depth;
+
+             /* Count the increment as a setting of the register,
+                even though it isn't a SET in rtl.  */
+             REG_N_SETS (regno)++;
            }
        }
     }
@@ -2019,8 +2299,8 @@ mark_used_regs (needed, live, x, final, insn)
      regset needed;
      regset live;
      rtx x;
-     rtx insn;
      int final;
+     rtx insn;
 {
   register RTX_CODE code;
   register int regno;
@@ -2036,7 +2316,6 @@ mark_used_regs (needed, live, x, final, insn)
     case CONST:
     case CONST_DOUBLE:
     case PC:
-    case CLOBBER:
     case ADDR_VEC:
     case ADDR_DIFF_VEC:
     case ASM_INPUT:
@@ -2048,10 +2327,23 @@ mark_used_regs (needed, live, x, final, insn)
       return;
 #endif
 
+    case CLOBBER:
+      /* If we are clobbering a MEM, mark any registers inside the address
+        as being used.  */
+      if (GET_CODE (XEXP (x, 0)) == MEM)
+       mark_used_regs (needed, live, XEXP (XEXP (x, 0), 0), final, insn);
+      return;
+
     case MEM:
-      /* Invalidate the data for the last MEM stored.  We could do this only
-        if the addresses conflict, but this doesn't seem worthwhile.  */
-      last_mem_set = 0;
+      /* CYGNUS LOCAL dje/8176 */
+      /* Invalidate the data for the last MEM stored, but only if MEM is
+        something that can be stored into.  */
+      if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+         && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)))
+       ; /* needn't clear last_mem_set */
+      else
+       last_mem_set = 0;
+      /* END CYGNUS LOCAL */
 
 #ifdef AUTO_INC_DEC
       if (final)
@@ -2059,28 +2351,51 @@ mark_used_regs (needed, live, x, final, insn)
 #endif
       break;
 
+    case SUBREG:
+      if (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)))))
+       REG_CHANGES_SIZE (REGNO (SUBREG_REG (x))) = 1;
+
+      /* While we're here, optimize this case.  */
+      x = SUBREG_REG (x);
+
+      /* In case the SUBREG is not of a register, don't optimize */
+      if (GET_CODE (x) != REG)
+       {
+         mark_used_regs (needed, live, x, final, insn);
+         return;
+       }
+
+      /* ... fall through ...  */
+
     case REG:
       /* See a register other than being set
         => mark it as needed.  */
 
       regno = REGNO (x);
       {
-       register int offset = regno / REGSET_ELT_BITS;
-       register int bit = 1 << (regno % REGSET_ELT_BITS);
-       int all_needed = (needed[offset] & bit) != 0;
-       int some_needed = (needed[offset] & bit) != 0;
+       REGSET_ELT_TYPE some_needed = REGNO_REG_SET_P (needed, regno);
+       REGSET_ELT_TYPE some_not_needed = ! some_needed;
+
+       SET_REGNO_REG_SET (live, regno);
 
-       live[offset] |= bit;
        /* A hard reg in a wide mode may really be multiple registers.
           If so, mark all of them just like the first.  */
        if (regno < FIRST_PSEUDO_REGISTER)
          {
            int n;
 
-           /* For stack ptr or arg pointer,
+           /* For stack ptr or fixed arg pointer,
               nothing below can be necessary, so waste no more time.  */
            if (regno == STACK_POINTER_REGNUM
-               || regno == ARG_POINTER_REGNUM
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+               || regno == HARD_FRAME_POINTER_REGNUM
+#endif
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+               || (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
+#endif
                || regno == FRAME_POINTER_REGNUM)
              {
                /* If this is a register we are going to try to eliminate,
@@ -2097,17 +2412,21 @@ mark_used_regs (needed, live, x, final, insn)
            /* No death notes for global register variables;
               their values are live after this function exits.  */
            if (global_regs[regno])
-             return;
+             {
+               if (final)
+                 reg_next_use[regno] = insn;
+               return;
+             }
 
            n = HARD_REGNO_NREGS (regno, GET_MODE (x));
            while (--n > 0)
              {
-               live[(regno + n) / REGSET_ELT_BITS]
-                 |= 1 << ((regno + n) % REGSET_ELT_BITS);
-               some_needed |= (needed[(regno + n) / REGSET_ELT_BITS]
-                               & 1 << ((regno + n) % REGSET_ELT_BITS));
-               all_needed &= (needed[(regno + n) / REGSET_ELT_BITS]
-                              & 1 << ((regno + n) % REGSET_ELT_BITS));
+               int regno_n = regno + n;
+               int needed_regno = REGNO_REG_SET_P (needed, regno_n);
+
+               SET_REGNO_REG_SET (live, regno_n);
+               some_needed |= needed_regno;
+               some_not_needed |= ! needed_regno;
              }
          }
        if (final)
@@ -2135,14 +2454,14 @@ mark_used_regs (needed, live, x, final, insn)
 
                register int blocknum = BLOCK_NUM (insn);
 
-               if (reg_basic_block[regno] == REG_BLOCK_UNKNOWN)
-                 reg_basic_block[regno] = blocknum;
-               else if (reg_basic_block[regno] != blocknum)
-                 reg_basic_block[regno] = REG_BLOCK_GLOBAL;
+               if (REG_BASIC_BLOCK (regno) == REG_BLOCK_UNKNOWN)
+                 REG_BASIC_BLOCK (regno) = blocknum;
+               else if (REG_BASIC_BLOCK (regno) != blocknum)
+                 REG_BASIC_BLOCK (regno) = REG_BLOCK_GLOBAL;
 
                /* Count (weighted) number of uses of each reg.  */
 
-               reg_n_refs[regno] += loop_depth;
+               REG_N_REFS (regno) += loop_depth;
              }
 
            /* Record and count the insns in which a reg dies.
@@ -2151,20 +2470,30 @@ mark_used_regs (needed, live, x, final, insn)
               we do not make a REG_DEAD note; likewise if we already
               made such a note.  */
 
-           if (! all_needed
+           if (some_not_needed
                && ! dead_or_set_p (insn, x)
 #if 0
                && (regno >= FIRST_PSEUDO_REGISTER || ! fixed_regs[regno])
 #endif
                )
              {
+               /* Check for the case where the register dying partially
+                  overlaps the register set by this insn.  */
+               if (regno < FIRST_PSEUDO_REGISTER
+                   && HARD_REGNO_NREGS (regno, GET_MODE (x)) > 1)
+                 {
+                   int n = HARD_REGNO_NREGS (regno, GET_MODE (x));
+                   while (--n >= 0)
+                     some_needed |= dead_or_set_regno_p (insn, regno + n);
+                 }
+
                /* If none of the words in X is needed, make a REG_DEAD
                   note.  Otherwise, we must make partial REG_DEAD notes.  */
                if (! some_needed)
                  {
                    REG_NOTES (insn)
                      = gen_rtx (EXPR_LIST, REG_DEAD, x, REG_NOTES (insn));
-                   reg_n_deaths[regno]++;
+                   REG_N_DEATHS (regno)++;
                  }
                else
                  {
@@ -2175,12 +2504,12 @@ mark_used_regs (needed, live, x, final, insn)
 
                    for (i = HARD_REGNO_NREGS (regno, GET_MODE (x)) - 1;
                         i >= 0; i--)
-                     if ((needed[(regno + i) / REGSET_ELT_BITS]
-                          & 1 << ((regno + i) % REGSET_ELT_BITS)) == 0
+                     if (!REGNO_REG_SET_P (needed, regno + i)
                          && ! dead_or_set_regno_p (insn, regno + i))
                        REG_NOTES (insn)
                          = gen_rtx (EXPR_LIST, REG_DEAD,
-                                    gen_rtx (REG, word_mode, regno + i),
+                                    gen_rtx (REG, reg_raw_mode[regno + i],
+                                             regno + i),
                                     REG_NOTES (insn));
                  }
              }
@@ -2218,6 +2547,13 @@ mark_used_regs (needed, live, x, final, insn)
               || GET_CODE (testreg) == SIGN_EXTRACT
               || GET_CODE (testreg) == SUBREG)
          {
+           if (GET_CODE (testreg) == SUBREG
+               && GET_CODE (SUBREG_REG (testreg)) == REG
+               && REGNO (SUBREG_REG (testreg)) >= FIRST_PSEUDO_REGISTER
+               && (GET_MODE_SIZE (GET_MODE (testreg))
+                   != GET_MODE_SIZE (GET_MODE (SUBREG_REG (testreg)))))
+             REG_CHANGES_SIZE (REGNO (SUBREG_REG (testreg))) = 1;
+
            /* Modifying a single register in an alternate mode
               does not use any of the old value.  But these other
               ways of storing in a register do use the old value.  */
@@ -2235,8 +2571,15 @@ mark_used_regs (needed, live, x, final, insn)
 
        if (GET_CODE (testreg) == REG
            && (regno = REGNO (testreg), regno != FRAME_POINTER_REGNUM)
-           && regno != ARG_POINTER_REGNUM
-           && ! (regno < FIRST_PSEUDO_REGISTER && global_regs[regno]))
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+           && regno != HARD_FRAME_POINTER_REGNUM
+#endif
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+           && ! (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
+#endif
+           )
+         /* We used to exclude global_regs here, but that seems wrong.
+            Storing in them is like storing in mem.  */
          {
            mark_used_regs (needed, live, SET_SRC (x), final, insn);
            if (mark_dest)
@@ -2249,18 +2592,21 @@ mark_used_regs (needed, live, x, final, insn)
     case RETURN:
       /* If exiting needs the right stack value, consider this insn as
         using the stack pointer.  In any event, consider it as using
-        all global registers.  */
+        all global registers and all registers used by return.  */
 
 #ifdef EXIT_IGNORE_STACK
       if (! EXIT_IGNORE_STACK
          || (! FRAME_POINTER_REQUIRED && flag_omit_frame_pointer))
 #endif
-       live[STACK_POINTER_REGNUM / REGSET_ELT_BITS]
-         |= 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS);
+       SET_REGNO_REG_SET (live, STACK_POINTER_REGNUM);
 
       for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-       if (global_regs[i])
-         live[i / REGSET_ELT_BITS] |= 1 << (i % REGSET_ELT_BITS);
+       if (global_regs[i]
+#ifdef EPILOGUE_USES
+           || EPILOGUE_USES (i)
+#endif
+           )
+         SET_REGNO_REG_SET (live, i);
       break;
     }
 
@@ -2301,12 +2647,15 @@ try_pre_increment_1 (insn)
   /* Find the next use of this reg.  If in same basic block,
      make it do pre-increment or pre-decrement if appropriate.  */
   rtx x = PATTERN (insn);
-  int amount = ((GET_CODE (SET_SRC (x)) == PLUS ? 1 : -1)
+  HOST_WIDE_INT amount = ((GET_CODE (SET_SRC (x)) == PLUS ? 1 : -1)
                * INTVAL (XEXP (SET_SRC (x), 1)));
   int regno = REGNO (SET_DEST (x));
   rtx y = reg_next_use[regno];
   if (y != 0
       && BLOCK_NUM (y) == BLOCK_NUM (insn)
+      /* Don't do this if the reg dies, or gets set in y; a standard addressing
+        mode would be better.  */
+      && ! dead_or_set_p (y, SET_DEST (x))
       && try_pre_increment (y, SET_DEST (PATTERN (insn)),
                            amount))
     {
@@ -2322,8 +2671,8 @@ try_pre_increment_1 (insn)
         less likely.  */
       if (regno >= FIRST_PSEUDO_REGISTER)
        {
-         reg_n_refs[regno] += loop_depth;
-         reg_n_sets[regno]++;
+         REG_N_REFS (regno) += loop_depth;
+         REG_N_SETS (regno)++;
        }
       return 1;
     }
@@ -2339,7 +2688,7 @@ try_pre_increment_1 (insn)
 static int
 try_pre_increment (insn, reg, amount)
      rtx insn, reg;
-     int amount;
+     HOST_WIDE_INT amount;
 {
   register rtx use;
 
@@ -2400,10 +2749,13 @@ try_pre_increment (insn, reg, amount)
   if (GET_MODE_SIZE (GET_MODE (use)) != (amount > 0 ? amount : - amount))
     return 0;
 
-  XEXP (use, 0) = gen_rtx (amount > 0
-                          ? (do_post ? POST_INC : PRE_INC)
-                          : (do_post ? POST_DEC : PRE_DEC),
-                          Pmode, reg);
+  /* See if this combination of instruction and addressing mode exists.  */
+  if (! validate_change (insn, &XEXP (use, 0),
+                        gen_rtx (amount > 0
+                                 ? (do_post ? POST_INC : PRE_INC)
+                                 : (do_post ? POST_DEC : PRE_DEC),
+                                 Pmode, reg), 0))
+    return 0;
 
   /* Record that this insn now has an implicit side effect on X.  */
   REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_INC, reg, REG_NOTES (insn));
@@ -2425,7 +2777,7 @@ static rtx
 find_use_as_address (x, reg, plusconst)
      register rtx x;
      rtx reg;
-     int plusconst;
+     HOST_WIDE_INT plusconst;
 {
   enum rtx_code code = GET_CODE (x);
   char *fmt = GET_RTX_FORMAT (code);
@@ -2447,11 +2799,11 @@ find_use_as_address (x, reg, plusconst)
       /* If REG occurs inside a MEM used in a bit-field reference,
         that is unacceptable.  */
       if (find_use_as_address (XEXP (x, 0), reg, 0) != 0)
-       return (rtx) 1;
+       return (rtx) (HOST_WIDE_INT) 1;
     }
 
   if (x == reg)
-    return (rtx) 1;
+    return (rtx) (HOST_WIDE_INT) 1;
 
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
     {
@@ -2461,7 +2813,7 @@ find_use_as_address (x, reg, plusconst)
          if (value == 0)
            value = tem;
          else if (tem != 0)
-           return (rtx) 1;
+           return (rtx) (HOST_WIDE_INT) 1;
        }
       if (fmt[i] == 'E')
        {
@@ -2472,7 +2824,7 @@ find_use_as_address (x, reg, plusconst)
              if (value == 0)
                value = tem;
              else if (tem != 0)
-               return (rtx) 1;
+               return (rtx) (HOST_WIDE_INT) 1;
            }
        }
     }
@@ -2493,28 +2845,33 @@ dump_flow_info (file)
   fprintf (file, "%d registers.\n", max_regno);
 
   for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
-    if (reg_n_refs[i])
+    if (REG_N_REFS (i))
       {
-       enum reg_class class;
+       enum reg_class class, altclass;
        fprintf (file, "\nRegister %d used %d times across %d insns",
-                i, reg_n_refs[i], reg_live_length[i]);
-       if (reg_basic_block[i] >= 0)
-         fprintf (file, " in block %d", reg_basic_block[i]);
-       if (reg_n_deaths[i] != 1)
-         fprintf (file, "; dies in %d places", reg_n_deaths[i]);
-       if (reg_n_calls_crossed[i] == 1)
+                i, REG_N_REFS (i), REG_LIVE_LENGTH (i));
+       if (REG_BASIC_BLOCK (i) >= 0)
+         fprintf (file, " in block %d", REG_BASIC_BLOCK (i));
+       if (REG_N_DEATHS (i) != 1)
+         fprintf (file, "; dies in %d places", REG_N_DEATHS (i));
+       if (REG_N_CALLS_CROSSED (i) == 1)
          fprintf (file, "; crosses 1 call");
-       else if (reg_n_calls_crossed[i])
-         fprintf (file, "; crosses %d calls", reg_n_calls_crossed[i]);
+       else if (REG_N_CALLS_CROSSED (i))
+         fprintf (file, "; crosses %d calls", REG_N_CALLS_CROSSED (i));
        if (PSEUDO_REGNO_BYTES (i) != UNITS_PER_WORD)
          fprintf (file, "; %d bytes", PSEUDO_REGNO_BYTES (i));
        class = reg_preferred_class (i);
-       if (class != GENERAL_REGS)
+       altclass = reg_alternate_class (i);
+       if (class != GENERAL_REGS || altclass != ALL_REGS)
          {
-           if (reg_preferred_or_nothing (i))
+           if (altclass == ALL_REGS || class == ALL_REGS)
+             fprintf (file, "; pref %s", reg_class_names[(int) class]);
+           else if (altclass == NO_REGS)
              fprintf (file, "; %s or none", reg_class_names[(int) class]);
            else
-             fprintf (file, "; pref %s", reg_class_names[(int) class]);
+             fprintf (file, "; pref %s, else %s",
+                      reg_class_names[(int) class],
+                      reg_class_names[(int) altclass]);
          }
        if (REGNO_POINTER_FLAG (i))
          fprintf (file, "; pointer");
@@ -2549,12 +2906,8 @@ dump_flow_info (file)
        }
       fprintf (file, "\nRegisters live at start:");
       for (regno = 0; regno < max_regno; regno++)
-       {
-         register int offset = regno / REGSET_ELT_BITS;
-         register int bit = 1 << (regno % REGSET_ELT_BITS);
-         if (basic_block_live_at_start[i][offset] & bit)
-             fprintf (file, " %d", regno);
-       }
+       if (REGNO_REG_SET_P (basic_block_live_at_start[i], regno))
+         fprintf (file, " %d", regno);
       fprintf (file, "\n");
     }
   fprintf (file, "\n");