flow.c (dump_flow_info): Also print number of sets and whether or not the pseudo...
[gcc.git] / gcc / flow.c
index 190cfc2ec8e6a0c95914b28eee7c8d0a8e1f097a..2598f8c3513318fbb8d17c8bd67177a4a32de8bb 100644 (file)
@@ -1,5 +1,5 @@
 /* Data flow analysis for GNU compiler.
-   Copyright (C) 1987, 88, 92-96, 1997 Free Software Foundation, Inc.
+   Copyright (C) 1987, 88, 92-97, 1998 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -108,8 +108,8 @@ Boston, MA 02111-1307, USA.  */
    register usage: reg_n_refs, reg_n_deaths, reg_n_sets, reg_live_length,
    reg_n_calls_crosses and reg_basic_block.  */
 \f
-#include <stdio.h>
 #include "config.h"
+#include "system.h"
 #include "rtl.h"
 #include "basic-block.h"
 #include "insn-config.h"
@@ -147,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 int *uid_block_number;
+int *uid_block_number;
 
 /* INSN_VOLATILE (insn) is 1 if the insn refers to anything volatile.  */
 
@@ -175,6 +175,10 @@ static int num_scratch;
 
 reg_info *reg_n_info;
 
+/* Size of the reg_n_info table.  */
+
+unsigned int reg_n_max;
+
 /* 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.
    This is valid only during the final backward scan in propagate_block.  */
@@ -197,6 +201,11 @@ rtx *basic_block_head;
 
 rtx *basic_block_end;
 
+/* Element N indicates whether basic block N can be reached through a
+   computed jump.  */
+
+char *basic_block_computed_jump_target;
+
 /* Element N is a regset describing the registers live
    at the start of basic block N.
    This info lasts until we finish compiling the function.  */
@@ -248,12 +257,12 @@ static rtx last_mem_set;
 static HARD_REG_SET elim_reg_set;
 
 /* Forward declarations */
-static void find_basic_blocks          PROTO((rtx, rtx));
-static int jmp_uses_reg_or_mem         PROTO((rtx));
+static void find_basic_blocks_1                PROTO((rtx, rtx, int));
 static void mark_label_ref             PROTO((rtx, rtx, int));
-static void life_analysis              PROTO((rtx, int));
+static void life_analysis_1            PROTO((rtx, int));
 void allocate_for_life_analysis                PROTO((void));
-static void init_regset_vector         PROTO((regset *, int, int, struct obstack *));
+void init_regset_vector                        PROTO((regset *, int, struct obstack *));
+void free_regset_vector                        PROTO((regset *, int));
 static void propagate_block            PROTO((regset, rtx, rtx, int, 
                                               regset, int));
 static rtx flow_delete_insn            PROTO((rtx));
@@ -263,53 +272,55 @@ static void mark_set_regs         PROTO((regset, regset, rtx,
                                               rtx, regset));
 static void mark_set_1                 PROTO((regset, regset, rtx,
                                               rtx, regset));
+#ifdef AUTO_INC_DEC
 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));
+#endif
+static void mark_used_regs             PROTO((regset, regset, rtx, int, rtx));
 void dump_flow_info                    PROTO((FILE *));
+static void add_pred_succ              PROTO ((int, int, int_list_ptr *,
+                                               int_list_ptr *, int *, int *));
+static int_list_ptr alloc_int_list_node PROTO ((int_list_block **));
+static int_list_ptr add_int_list_node   PROTO ((int_list_block **,
+                                               int_list **, int));
 \f
-/* Find basic blocks of the current function and perform data flow analysis.
+/* Find basic blocks of the current function.
    F is the first insn of the function and NREGS the number of register numbers
-   in use.  */
+   in use.
+   LIVE_REACHABLE_P is non-zero if the caller needs all live blocks to
+   be reachable.  This turns on a kludge that causes the control flow
+   information to be inaccurate and not suitable for passes like GCSE.  */
 
 void
-flow_analysis (f, nregs, file)
+find_basic_blocks (f, nregs, file, live_reachable_p)
      rtx f;
      int nregs;
      FILE *file;
+     int live_reachable_p;
 {
   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.  */
-
-  CLEAR_HARD_REG_SET (elim_reg_set);
-
-#ifdef ELIMINABLE_REGS
-  for (i = 0; i < sizeof eliminables / sizeof eliminables[0]; i++)
-    SET_HARD_REG_BIT (elim_reg_set, eliminables[i].from);
-#else
-  SET_HARD_REG_BIT (elim_reg_set, FRAME_POINTER_REGNUM);
-#endif
+  int in_libcall_block = 0;
 
   /* Count the basic blocks.  Also find maximum insn uid value used.  */
 
   {
     register RTX_CODE prev_code = JUMP_INSN;
     register RTX_CODE code;
+    int eh_region = 0;
 
     max_uid_for_flow = 0;
 
     for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
       {
+
+       /* Track when we are inside in LIBCALL block.  */
+       if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+           && find_reg_note (insn, REG_LIBCALL, NULL_RTX))
+         in_libcall_block = 1;
+
        code = GET_CODE (insn);
        if (INSN_UID (insn) > max_uid_for_flow)
          max_uid_for_flow = INSN_UID (insn);
@@ -317,7 +328,8 @@ flow_analysis (f, nregs, file)
            || (GET_RTX_CLASS (code) == 'i'
                && (prev_code == JUMP_INSN
                    || (prev_code == CALL_INSN
-                       && nonlocal_label_list != 0)
+                       && (nonlocal_label_list != 0 || eh_region)
+                       && ! in_libcall_block)
                    || prev_code == BARRIER)))
          i++;
 
@@ -326,69 +338,84 @@ flow_analysis (f, nregs, file)
 
        if (code != NOTE)
          prev_code = code;
+       else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG)
+         ++eh_region;
+       else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END)
+         --eh_region;
+
+       if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+           && find_reg_note (insn, REG_RETVAL, NULL_RTX))
+         in_libcall_block = 0;
       }
   }
 
+  n_basic_blocks = i;
+
 #ifdef AUTO_INC_DEC
-  /* Leave space for insns we make in some cases for auto-inc.  These cases
-     are rare, so we don't need too much space.  */
+  /* Leave space for insns life_analysis makes in some cases for auto-inc.
+     These cases are rare, so we don't need too much space.  */
   max_uid_for_flow += max_uid_for_flow / 10;
 #endif
 
   /* Allocate some tables that last till end of compiling this function
      and some needed only in find_basic_blocks and life_analysis.  */
 
-  n_basic_blocks = i;
-  basic_block_head = (rtx *) oballoc (n_basic_blocks * sizeof (rtx));
-  basic_block_end = (rtx *) oballoc (n_basic_blocks * sizeof (rtx));
-  basic_block_drops_in = (char *) alloca (n_basic_blocks);
-  basic_block_loop_depth = (short *) alloca (n_basic_blocks * sizeof (short));
+  basic_block_head = (rtx *) xmalloc (n_basic_blocks * sizeof (rtx));
+  basic_block_end = (rtx *) xmalloc (n_basic_blocks * sizeof (rtx));
+  basic_block_drops_in = (char *) xmalloc (n_basic_blocks);
+  basic_block_computed_jump_target = (char *) oballoc (n_basic_blocks);
+  basic_block_loop_depth = (short *) xmalloc (n_basic_blocks * sizeof (short));
   uid_block_number
-    = (int *) alloca ((max_uid_for_flow + 1) * sizeof (int));
-  uid_volatile = (char *) alloca (max_uid_for_flow + 1);
+    = (int *) xmalloc ((max_uid_for_flow + 1) * sizeof (int));
+  uid_volatile = (char *) xmalloc (max_uid_for_flow + 1);
   bzero (uid_volatile, max_uid_for_flow + 1);
 
-  find_basic_blocks (f, nonlocal_label_list);
-  life_analysis (f, nregs);
-  if (file)
-    dump_flow_info (file);
-
-  basic_block_drops_in = 0;
-  uid_block_number = 0;
-  basic_block_loop_depth = 0;
+  find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p);
 }
-\f
+
 /* 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.
 
-   NONLOCAL_LABEL_LIST is the same local variable from flow_analysis.  */
+   NONLOCAL_LABEL_LIST is a list of non-local labels in the function.
+   Blocks that are otherwise unreachable may be reachable with a non-local
+   goto.
+   LIVE_REACHABLE_P is non-zero if the caller needs all live blocks to
+   be reachable.  This turns on a kludge that causes the control flow
+   information to be inaccurate and not suitable for passes like GCSE.  */
 
 static void
-find_basic_blocks (f, nonlocal_label_list)
+find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p)
      rtx f, nonlocal_label_list;
+     int live_reachable_p;
 {
   register rtx insn;
   register int i;
   register char *block_live = (char *) alloca (n_basic_blocks);
   register char *block_marked = (char *) alloca (n_basic_blocks);
+  /* An array of CODE_LABELs, indexed by UID for the start of the active
+     EH handler for each insn in F.  */
+  rtx *active_eh_handler;
   /* List of label_refs to all labels whose addresses are taken
      and used as data.  */
   rtx label_value_list;
-  int label_value_list_marked_live;
-  rtx x, note;
+  rtx x, note, eh_note;
   enum rtx_code prev_code, code;
   int depth, pass;
+  int in_libcall_block = 0;
 
   pass = 1;
+  active_eh_handler = (rtx *) alloca ((max_uid_for_flow + 1) * sizeof (rtx));
  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);
+  bzero (basic_block_computed_jump_target, n_basic_blocks);
+  bzero (active_eh_handler, (max_uid_for_flow + 1) * sizeof (rtx));
+  current_function_has_computed_jump = 0;
 
   /* Initialize with just block 0 reachable and no blocks marked.  */
   if (n_basic_blocks > 0)
@@ -399,9 +426,15 @@ find_basic_blocks (f, nonlocal_label_list)
      the block it is in.   Also mark as reachable any blocks headed by labels
      that must not be deleted.  */
 
-  for (insn = f, i = -1, prev_code = JUMP_INSN, depth = 1;
+  for (eh_note = NULL_RTX, insn = f, i = -1, prev_code = JUMP_INSN, depth = 1;
        insn; insn = NEXT_INSN (insn))
     {
+
+      /* Track when we are inside in LIBCALL block.  */
+      if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+         && find_reg_note (insn, REG_LIBCALL, NULL_RTX))
+       in_libcall_block = 1;
+
       code = GET_CODE (insn);
       if (code == NOTE)
        {
@@ -416,8 +449,8 @@ find_basic_blocks (f, nonlocal_label_list)
               || (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))
+                          && (nonlocal_label_list != 0 || eh_note)
+                          && ! in_libcall_block)
                       || prev_code == BARRIER)))
        {
          basic_block_head[++i] = insn;
@@ -445,14 +478,47 @@ find_basic_blocks (f, nonlocal_label_list)
          /* 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);
+             label_value_list = gen_rtx_EXPR_LIST (VOIDmode, XEXP (note, 0),
+                                                   label_value_list);
+       }
+
+      /* Keep a lifo list of the currently active exception handlers.  */
+      if (GET_CODE (insn) == NOTE)
+       {
+         if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG)
+           {
+             for (x = exception_handler_labels; x; x = XEXP (x, 1))
+               if (CODE_LABEL_NUMBER (XEXP (x, 0)) == NOTE_BLOCK_NUMBER (insn))
+                 {
+                   eh_note = gen_rtx_EXPR_LIST (VOIDmode,
+                                                XEXP (x, 0), eh_note);
+                   break;
+                 }
+             if (x == NULL_RTX)
+               abort ();
+           }
+         else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END)
+           eh_note = XEXP (eh_note, 1);
        }
+      /* If we encounter a CALL_INSN, note which exception handler it
+        might pass control to.
+
+        If doing asynchronous exceptions, record the active EH handler
+        for every insn, since most insns can throw.  */
+      else if (eh_note
+              && (asynchronous_exceptions
+                  || (GET_CODE (insn) == CALL_INSN
+                      && ! in_libcall_block)))
+       active_eh_handler[INSN_UID (insn)] = XEXP (eh_note, 0);
 
       BLOCK_NUM (insn) = i;
 
       if (code != NOTE)
        prev_code = code;
+
+      if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+         && find_reg_note (insn, REG_RETVAL, NULL_RTX))
+       in_libcall_block = 0;
     }
 
   /* During the second pass, `n_basic_blocks' is only an upper bound.
@@ -462,13 +528,6 @@ find_basic_blocks (f, nonlocal_label_list)
     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.  */
 
   for (i = 0; i < n_basic_blocks; i++)
@@ -489,99 +548,6 @@ find_basic_blocks (f, nonlocal_label_list)
       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.
-
-        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)
-         {
-           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);
-             }
-         }
-
-      /* 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.
@@ -602,43 +568,134 @@ find_basic_blocks (f, nonlocal_label_list)
                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 != 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 we have any forced labels, mark them as potentially
+                  reachable from this block.  */
+               for (x = forced_labels; x; x = XEXP (x, 1))
+                 if (! LABEL_REF_NONLOCAL_P (x))
+                   mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, XEXP (x, 0)),
+                                   insn, 0);
+
+               /* Now scan the insns for this block, we may need to make
+                  edges for some of them to various non-obvious locations
+                  (exception handlers, nonlocal labels, etc).  */
+               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')
+                     {
+                       
+                       /* References to labels in non-jumping insns have
+                          REG_LABEL notes attached to them.
+
+                          This can happen for computed gotos; we don't care
+                          about them here since the values are also on the
+                          label_value_list and will be marked live if we find
+                          a live computed goto.
+
+                          This can also happen when we take the address of
+                          a label to pass as an argument to __throw.  Note
+                          throw only uses the value to determine what handler
+                          should be called -- ie the label is not used as
+                          a jump target, it just marks regions in the code.
+
+                          In theory we should be able to ignore the REG_LABEL
+                          notes, but we have to make sure that the label and
+                          associated insns aren't marked dead, so we make
+                          the block in question live and create an edge from
+                          this insn to the label.  This is not strictly
+                          correct, but it is close enough for now.  */
+                       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;
+                               mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, x),
+                                               insn, 0);
                              }
-                       }
-                   }
+                         }
+
+                       /* If this is a computed jump, then mark it as
+                          reaching everything on the label_value_list
+                          and forced_labels list.  */
+                       if (computed_jump_p (insn))
+                         {
+                           current_function_has_computed_jump = 1;
+                           for (x = label_value_list; x; x = XEXP (x, 1))
+                             {
+                               int b = BLOCK_NUM (XEXP (x, 0));
+                               basic_block_computed_jump_target[b] = 1;
+                               mark_label_ref (gen_rtx_LABEL_REF (VOIDmode,
+                                                                  XEXP (x, 0)),
+                                               insn, 0);
+                             }
+
+                           for (x = forced_labels; x; x = XEXP (x, 1))
+                             {
+                               int b = BLOCK_NUM (XEXP (x, 0));
+                               basic_block_computed_jump_target[b] = 1;
+                               mark_label_ref (gen_rtx_LABEL_REF (VOIDmode,
+                                                                  XEXP (x, 0)),
+                                               insn, 0);
+                             }
+                         }
+
+                       /* If this is a CALL_INSN, then mark it as reaching
+                          the active EH handler for this CALL_INSN.  If
+                          we're handling asynchronous exceptions mark every
+                          insn as reaching the active EH handler.
+
+                          Also mark the CALL_INSN as reaching any nonlocal
+                          goto sites.  */
+                       else if (asynchronous_exceptions
+                                || (GET_CODE (insn) == CALL_INSN
+                                    && ! find_reg_note (insn, REG_RETVAL,
+                                                        NULL_RTX)))
+                         {
+                           if (active_eh_handler[INSN_UID (insn)])
+                             mark_label_ref (gen_rtx_LABEL_REF (VOIDmode,
+                                                                active_eh_handler[INSN_UID (insn)]),
+                                             insn, 0);
+
+                           if (!asynchronous_exceptions)
+                             {
+                               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.  */
+                         }
+                     }
+                 }
              }
        }
 
-      /* ??? 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.  */
+      /* This should never happen.  If it does that means we've computed an
+        incorrect flow graph, which can lead to aborts/crashes later in the
+        compiler or incorrect code generation.
 
+        We used to try and continue here, but that's just asking for trouble
+        later during the compile or at runtime.  It's easier to debug the
+        problem here than later!  */
       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;
+         abort ();
 
       /* Now delete the code for any basic blocks that can't be reached.
         They can occur because jump_optimize does not recognize
@@ -682,6 +739,51 @@ find_basic_blocks (f, nonlocal_label_list)
                /* Turn the head into a deleted insn note.  */
                if (GET_CODE (insn) == BARRIER)
                  abort ();
+
+               /* If the head of this block is a CODE_LABEL, then it might
+                  be the label for an exception handler which can't be
+                  reached.
+
+                  We need to remove the label from the exception_handler_label
+                  list and remove the associated NOTE_EH_REGION_BEG and
+                  NOTE_EH_REGION_END notes.  */
+               if (GET_CODE (insn) == CODE_LABEL)
+                 {
+                   rtx x, *prev = &exception_handler_labels;
+
+                   for (x = exception_handler_labels; x; x = XEXP (x, 1))
+                     {
+                       if (XEXP (x, 0) == insn)
+                         {
+                           /* Found a match, splice this label out of the
+                              EH label list.  */
+                           *prev = XEXP (x, 1);
+                           XEXP (x, 1) = NULL_RTX;
+                           XEXP (x, 0) = NULL_RTX;
+
+                           /* Now we have to find the EH_BEG and EH_END notes
+                              associated with this label and remove them.  */
+
+                           for (x = get_insns (); x; x = NEXT_INSN (x))
+                             {
+                               if (GET_CODE (x) == NOTE
+                                   && ((NOTE_LINE_NUMBER (x)
+                                        == NOTE_INSN_EH_REGION_BEG)
+                                       || (NOTE_LINE_NUMBER (x)
+                                           == NOTE_INSN_EH_REGION_END))
+                                   && (NOTE_BLOCK_NUMBER (x)
+                                       == CODE_LABEL_NUMBER (insn)))
+                                 {
+                                   NOTE_LINE_NUMBER (x) = NOTE_INSN_DELETED;
+                                   NOTE_SOURCE_FILE (x) = 0;
+                                 }
+                             }
+                           break;
+                         }
+                       prev = &XEXP (x, 1);
+                     }
+                 }
+                
                PUT_CODE (insn, NOTE);
                NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
                NOTE_SOURCE_FILE (insn) = 0;
@@ -726,6 +828,14 @@ find_basic_blocks (f, nonlocal_label_list)
                          && INSN_UID (label) != 0
                          && BLOCK_NUM (label) == j)
                        {
+                         int k;
+
+                         /* The deleted blocks still show up in the cfg,
+                            so we must set basic_block_drops_in for blocks
+                            I to J inclusive to keep the cfg accurate.  */
+                         for (k = i; k <= j; k++)
+                           basic_block_drops_in[k] = 1;
+
                          PUT_CODE (insn, NOTE);
                          NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
                          NOTE_SOURCE_FILE (insn) = 0;
@@ -766,59 +876,27 @@ find_basic_blocks (f, nonlocal_label_list)
        }
     }
 }
-\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.  */
+/* Record INSN's block number as BB.  */
 
-static int
-jmp_uses_reg_or_mem (x)
-     rtx x;
+void
+set_block_num (insn, bb)
+     rtx insn;
+     int bb;
 {
-  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 (INSN_UID (insn) >= max_uid_for_flow)
     {
-      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;
+      /* Add one-eighth the size so we don't keep calling xrealloc.  */
+      max_uid_for_flow = INSN_UID (insn) + (INSN_UID (insn) + 7) / 8;
+      uid_block_number = (int *)
+       xrealloc (uid_block_number, (max_uid_for_flow + 1) * sizeof (int));
     }
-
-  return 0;
+  BLOCK_NUM (insn) = bb;
 }
 
+\f
+/* Subroutines of find_basic_blocks.  */
+
 /* Check expression X for label references;
    if one is found, add INSN to the label's chain of references.
 
@@ -894,6 +972,80 @@ flow_delete_insn (insn)
   return NEXT_INSN (insn);
 }
 \f
+/* Perform data flow analysis.
+   F is the first insn of the function and NREGS the number of register numbers
+   in use.  */
+
+void
+life_analysis (f, nregs, file)
+     rtx f;
+     int nregs;
+     FILE *file;
+{
+#ifdef ELIMINABLE_REGS
+  register size_t i;
+  static struct {int from, to; } eliminables[] = ELIMINABLE_REGS;
+#endif
+
+  /* Record which registers will be eliminated.  We use this in
+     mark_used_regs.  */
+
+  CLEAR_HARD_REG_SET (elim_reg_set);
+
+#ifdef ELIMINABLE_REGS
+  for (i = 0; i < sizeof eliminables / sizeof eliminables[0]; i++)
+    SET_HARD_REG_BIT (elim_reg_set, eliminables[i].from);
+#else
+  SET_HARD_REG_BIT (elim_reg_set, FRAME_POINTER_REGNUM);
+#endif
+
+  life_analysis_1 (f, nregs);
+  if (file)
+    dump_flow_info (file);
+
+  free_basic_block_vars (1);
+}
+
+/* Free the variables allocated by find_basic_blocks.
+
+   KEEP_HEAD_END_P is non-zero if basic_block_head and basic_block_end
+   are not to be freed.  */
+
+void
+free_basic_block_vars (keep_head_end_p)
+     int keep_head_end_p;
+{
+  if (basic_block_drops_in)
+    {
+      free (basic_block_drops_in);
+      /* Tell dump_flow_info this isn't available anymore.  */
+      basic_block_drops_in = 0;
+    }
+  if (basic_block_loop_depth)
+    {
+      free (basic_block_loop_depth);
+      basic_block_loop_depth = 0;
+    }
+  if (uid_block_number)
+    {
+      free (uid_block_number);
+      uid_block_number = 0;
+    }
+  if (uid_volatile)
+    {
+      free (uid_volatile);
+      uid_volatile = 0;
+    }
+
+  if (! keep_head_end_p && basic_block_head)
+    {
+      free (basic_block_head);
+      basic_block_head = 0;
+      free (basic_block_end);
+      basic_block_end = 0;
+    }
+}
+
 /* Determine which registers are live at the start of each
    basic block of the function whose first insn is F.
    NREGS is the number of registers used in F.
@@ -902,7 +1054,7 @@ flow_delete_insn (insn)
    regset_size and regset_bytes are also set here.  */
 
 static void
-life_analysis (f, nregs)
+life_analysis_1 (f, nregs)
      rtx f;
      int nregs;
 {
@@ -951,18 +1103,16 @@ life_analysis (f, nregs)
      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.  */
-  init_regset_vector (basic_block_live_at_end, n_basic_blocks, regset_bytes,
-                     &flow_obstack);
+  init_regset_vector (basic_block_live_at_end, n_basic_blocks, &flow_obstack);
 
   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,
+  init_regset_vector (basic_block_new_live_at_end, n_basic_blocks,
                      &flow_obstack);
 
   basic_block_significant
     = (regset *) alloca (n_basic_blocks * sizeof (regset));
-  init_regset_vector (basic_block_significant, n_basic_blocks, regset_bytes,
-                     &flow_obstack);
+  init_regset_vector (basic_block_significant, n_basic_blocks, &flow_obstack);
 
   /* Record which insns refer to any volatile memory
      or for any reason can't be deleted just because they are dead stores.
@@ -1058,7 +1208,9 @@ life_analysis (f, nregs)
   if (n_basic_blocks > 0)
 #ifdef EXIT_IGNORE_STACK
     if (! EXIT_IGNORE_STACK
-       || (! FRAME_POINTER_REQUIRED && flag_omit_frame_pointer))
+       || (! FRAME_POINTER_REQUIRED
+           && ! current_function_calls_alloca
+           && flag_omit_frame_pointer))
 #endif
       {
        /* If exiting needs the right stack value,
@@ -1136,17 +1288,17 @@ 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).  */
 
-             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;
-                                                  }
-                                              });
+             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;
@@ -1286,6 +1438,14 @@ life_analysis (f, nregs)
                                 }
                             });
 
+
+  free_regset_vector (basic_block_live_at_end, n_basic_blocks);
+  free_regset_vector (basic_block_new_live_at_end, n_basic_blocks);
+  free_regset_vector (basic_block_significant, n_basic_blocks);
+  basic_block_live_at_end = (regset *)0;
+  basic_block_new_live_at_end = (regset *)0;
+  basic_block_significant = (regset *)0;
+
   obstack_free (&flow_obstack, NULL_PTR);
 }
 \f
@@ -1299,37 +1459,34 @@ allocate_for_life_analysis ()
 {
   register int i;
 
-  regset_size = ((max_regno + REGSET_ELT_BITS - 1) / REGSET_ELT_BITS);
-  regset_bytes = regset_size * sizeof (*(regset) 0);
+  /* Recalculate the register space, in case it has grown.  Old style
+     vector oriented regsets would set regset_{size,bytes} here also.  */
+  allocate_reg_info (max_regno, FALSE, FALSE);
 
   /* 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);
-
   for (i = 0; i < max_regno; i++)
     REG_N_SETS (i) = 0;
 
   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,
+  init_regset_vector (basic_block_live_at_start, n_basic_blocks,
                      function_obstack);
 
   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,
-   taking the space for all those regsets from SPACE.
-   SPACE is of type regset, but it is really as long as NELTS regsets.
-   BYTES_PER_ELT is the number of bytes in one regset.  */
+/* Make each element of VECTOR point at a regset.  The vector has
+   NELTS elements, and space is allocated from the ALLOC_OBSTACK
+   obstack.  */
 
-static void
-init_regset_vector (vector, nelts, bytes_per_elt, alloc_obstack)
+void
+init_regset_vector (vector, nelts, alloc_obstack)
      regset *vector;
      int nelts;
-     int bytes_per_elt;
      struct obstack *alloc_obstack;
 {
   register int i;
@@ -1341,6 +1498,20 @@ init_regset_vector (vector, nelts, bytes_per_elt, alloc_obstack)
     }
 }
 
+/* Release any additional space allocated for each element of VECTOR point
+   other than the regset header itself.  The vector has NELTS elements.  */
+
+void
+free_regset_vector (vector, nelts)
+     regset *vector;
+     int nelts;
+{
+  register int i;
+
+  for (i = 0; i < nelts; i++)
+    FREE_REG_SET (vector[i]);
+}
+
 /* Compute the registers live at the beginning of a basic block
    from those live at the end.
 
@@ -1515,9 +1686,10 @@ propagate_block (old, first, last, final, significant, bnum)
             merged into a following memory address.  */
 #ifdef AUTO_INC_DEC
          {
-           register rtx x = PATTERN (insn);
+           register rtx x = single_set (insn);
+
            /* Does this instruction increment or decrement a register?  */
-           if (final && GET_CODE (x) == SET
+           if (final && x != 0
                && GET_CODE (SET_DEST (x)) == REG
                && (GET_CODE (SET_SRC (x)) == PLUS
                    || GET_CODE (SET_SRC (x)) == MINUS)
@@ -1609,7 +1781,7 @@ propagate_block (old, first, last, final, significant, bnum)
                  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
                    if (global_regs[i])
                      mark_used_regs (old, live,
-                                     gen_rtx (REG, reg_raw_mode[i], i),
+                                     gen_rtx_REG (reg_raw_mode[i], i),
                                      final, insn);
 
                  /* Calls also clobber memory.  */
@@ -1643,11 +1815,12 @@ propagate_block (old, first, last, final, significant, bnum)
              register int regno;
              register int *p;
 
-             EXECUTE_IF_AND_COMPL_IN_REG_SET (live, maxlive, 0, regno,
-                                              {
-                                                regs_sometimes_live[sometimes_max++] = regno;
-                                                SET_REGNO_REG_SET (maxlive, regno);
-                                              });
+             EXECUTE_IF_AND_COMPL_IN_REG_SET
+               (live, maxlive, 0, regno,
+                {
+                  regs_sometimes_live[sometimes_max++] = regno;
+                  SET_REGNO_REG_SET (maxlive, regno);
+                });
 
              p = regs_sometimes_live;
              for (i = 0; i < sometimes_max; i++)
@@ -1663,6 +1836,11 @@ propagate_block (old, first, last, final, significant, bnum)
        break;
     }
 
+  FREE_REG_SET (dead);
+  FREE_REG_SET (live);
+  if (final)
+    FREE_REG_SET (maxlive);
+
   if (num_scratch > max_scratch)
     max_scratch = num_scratch;
 }
@@ -1679,13 +1857,15 @@ insn_dead_p (x, needed, call_ok)
      regset needed;
      int call_ok;
 {
-  register RTX_CODE code = GET_CODE (x);
+  enum rtx_code code = GET_CODE (x);
+
   /* If setting something that's a reg or part of one,
      see if that register's altered value will be live.  */
 
   if (code == SET)
     {
-      register rtx r = SET_DEST (x);
+      rtx r = SET_DEST (x);
+
       /* A SET that is a subroutine call cannot be dead.  */
       if (! call_ok && GET_CODE (SET_SRC (x)) == CALL)
        return 0;
@@ -1699,15 +1879,13 @@ insn_dead_p (x, needed, call_ok)
          && rtx_equal_p (r, last_mem_set))
        return 1;
 
-      while (GET_CODE (r) == SUBREG
-            || GET_CODE (r) == STRICT_LOW_PART
-            || GET_CODE (r) == ZERO_EXTRACT
-            || GET_CODE (r) == SIGN_EXTRACT)
+      while (GET_CODE (r) == SUBREG || GET_CODE (r) == STRICT_LOW_PART
+            || GET_CODE (r) == ZERO_EXTRACT)
        r = SUBREG_REG (r);
 
       if (GET_CODE (r) == REG)
        {
-         register int regno = REGNO (r);
+         int regno = REGNO (r);
 
          /* Don't delete insns to set global regs.  */
          if ((regno < FIRST_PSEUDO_REGISTER && global_regs[regno])
@@ -1739,26 +1917,33 @@ insn_dead_p (x, needed, call_ok)
          return 1;
        }
     }
+
   /* If performing several activities,
      insn is dead if each activity is individually dead.
      Also, CLOBBERs and USEs can be ignored; a CLOBBER or USE
      that's inside a PARALLEL doesn't make the insn worth keeping.  */
   else if (code == PARALLEL)
     {
-      register int i = XVECLEN (x, 0);
+      int i = XVECLEN (x, 0);
+
       for (i--; i >= 0; i--)
-       {
-         rtx elt = XVECEXP (x, 0, i);
-         if (!insn_dead_p (elt, needed, call_ok)
-             && GET_CODE (elt) != CLOBBER
-             && GET_CODE (elt) != USE)
-           return 0;
-       }
+       if (GET_CODE (XVECEXP (x, 0, i)) != CLOBBER
+           && GET_CODE (XVECEXP (x, 0, i)) != USE
+           && ! insn_dead_p (XVECEXP (x, 0, i), needed, call_ok))
+         return 0;
+
       return 1;
     }
-  /* We do not check CLOBBER or USE here.
-     An insn consisting of just a CLOBBER or just a USE
-     should not be deleted.  */
+
+  /* A CLOBBER of a pseudo-register that is dead serves no purpose.  That
+     is not necessarily true for hard registers.  */
+  else if (code == CLOBBER && GET_CODE (XEXP (x, 0)) == REG
+          && REGNO (XEXP (x, 0)) >= FIRST_PSEUDO_REGISTER
+          && ! REGNO_REG_SET_P (needed, REGNO (XEXP (x, 0))))
+    return 1;
+
+  /* We do not check other CLOBBER or USE here.  An insn consisting of just
+     a CLOBBER or just a USE should not be deleted.  */
   return 0;
 }
 
@@ -1957,7 +2142,7 @@ mark_set_1 (needed, dead, x, insn, significant)
       if (significant)
        SET_REGNO_REG_SET (significant, regno);
 
-      /* Mark it as as dead before this insn.  */
+      /* Mark it as dead before this insn.  */
       SET_REGNO_REG_SET (dead, regno);
 
       /* A hard reg in a wide mode may really be multiple registers.
@@ -2048,7 +2233,7 @@ mark_set_1 (needed, dead, x, insn, significant)
                  && (regno >= FIRST_PSEUDO_REGISTER
                      || asm_noperands (PATTERN (y)) < 0))
                LOG_LINKS (y)
-                 = gen_rtx (INSN_LIST, VOIDmode, insn, LOG_LINKS (y));
+                 = gen_rtx_INSN_LIST (VOIDmode, insn, LOG_LINKS (y));
            }
          else if (! some_needed)
            {
@@ -2057,7 +2242,7 @@ mark_set_1 (needed, dead, x, insn, significant)
                 be eliminated (because the same insn does something useful).
                 Indicate this by marking the reg being set as dying here.  */
              REG_NOTES (insn)
-               = gen_rtx (EXPR_LIST, REG_UNUSED, reg, REG_NOTES (insn));
+               = gen_rtx_EXPR_LIST (REG_UNUSED, reg, REG_NOTES (insn));
              REG_N_DEATHS (REGNO (reg))++;
            }
          else
@@ -2074,10 +2259,10 @@ mark_set_1 (needed, dead, x, insn, significant)
                   i >= 0; i--)
                if (!REGNO_REG_SET_P (needed, regno + i))
                  REG_NOTES (insn)
-                   = gen_rtx (EXPR_LIST, REG_UNUSED,
-                              gen_rtx (REG, reg_raw_mode[regno + i],
-                                       regno + i),
-                              REG_NOTES (insn));
+                   = gen_rtx_EXPR_LIST (REG_UNUSED,
+                                        gen_rtx_REG (reg_raw_mode[regno + i],
+                                                     regno + i),
+                                        REG_NOTES (insn));
            }
        }
     }
@@ -2089,7 +2274,7 @@ mark_set_1 (needed, dead, x, insn, significant)
   else if (GET_CODE (reg) == SCRATCH && insn != 0)
     {
       REG_NOTES (insn)
-       = gen_rtx (EXPR_LIST, REG_UNUSED, reg, REG_NOTES (insn));
+       = gen_rtx_EXPR_LIST (REG_UNUSED, reg, REG_NOTES (insn));
       num_scratch++;
     }
 }
@@ -2163,7 +2348,7 @@ find_auto_inc (needed, x, insn)
                 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),
+                                    gen_rtx_fmt_e (inc_code, Pmode, addr),
                                     0))
                return;
            }
@@ -2204,7 +2389,7 @@ find_auto_inc (needed, x, insn)
                 so is not correct in the pre-inc case.  */
 
              validate_change (insn, &XEXP (x, 0),
-                              gen_rtx (inc_code, Pmode, q),
+                              gen_rtx_fmt_e (inc_code, Pmode, q),
                               1);
              validate_change (incr, &XEXP (y, 0), q, 1);
              if (! apply_change_group ())
@@ -2251,7 +2436,7 @@ find_auto_inc (needed, x, insn)
             has an implicit side effect.  */
 
          REG_NOTES (insn)
-           = gen_rtx (EXPR_LIST, REG_INC, addr, 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.  */
@@ -2335,7 +2520,6 @@ mark_used_regs (needed, live, x, final, insn)
       return;
 
     case MEM:
-      /* 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
@@ -2343,7 +2527,6 @@ mark_used_regs (needed, live, x, final, insn)
        ; /* needn't clear last_mem_set */
       else
        last_mem_set = 0;
-      /* END CYGNUS LOCAL */
 
 #ifdef AUTO_INC_DEC
       if (final)
@@ -2376,8 +2559,8 @@ mark_used_regs (needed, live, x, final, insn)
 
       regno = REGNO (x);
       {
-       REGSET_ELT_TYPE some_needed = REGNO_REG_SET_P (needed, regno);
-       REGSET_ELT_TYPE some_not_needed = ! some_needed;
+       int some_needed = REGNO_REG_SET_P (needed, regno);
+       int some_not_needed = ! some_needed;
 
        SET_REGNO_REG_SET (live, regno);
 
@@ -2492,7 +2675,7 @@ mark_used_regs (needed, live, x, final, insn)
                if (! some_needed)
                  {
                    REG_NOTES (insn)
-                     = gen_rtx (EXPR_LIST, REG_DEAD, x, REG_NOTES (insn));
+                     = gen_rtx_EXPR_LIST (REG_DEAD, x, REG_NOTES (insn));
                    REG_N_DEATHS (regno)++;
                  }
                else
@@ -2507,10 +2690,10 @@ mark_used_regs (needed, live, x, final, insn)
                      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, reg_raw_mode[regno + i],
-                                             regno + i),
-                                    REG_NOTES (insn));
+                         = gen_rtx_EXPR_LIST (REG_DEAD,
+                                              gen_rtx_REG (reg_raw_mode[regno + i],
+                                                           regno + i),
+                                              REG_NOTES (insn));
                  }
              }
          }
@@ -2596,7 +2779,9 @@ mark_used_regs (needed, live, x, final, insn)
 
 #ifdef EXIT_IGNORE_STACK
       if (! EXIT_IGNORE_STACK
-         || (! FRAME_POINTER_REQUIRED && flag_omit_frame_pointer))
+         || (! FRAME_POINTER_REQUIRED
+             && ! current_function_calls_alloca
+             && flag_omit_frame_pointer))
 #endif
        SET_REGNO_REG_SET (live, STACK_POINTER_REGNUM);
 
@@ -2608,6 +2793,9 @@ mark_used_regs (needed, live, x, final, insn)
            )
          SET_REGNO_REG_SET (live, i);
       break;
+
+    default:
+      break;
     }
 
   /* Recursively scan the operands of this expression.  */
@@ -2646,7 +2834,7 @@ 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);
+  rtx x = single_set (insn);
   HOST_WIDE_INT amount = ((GET_CODE (SET_SRC (x)) == PLUS ? 1 : -1)
                * INTVAL (XEXP (SET_SRC (x), 1)));
   int regno = REGNO (SET_DEST (x));
@@ -2656,8 +2844,7 @@ try_pre_increment_1 (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))
+      && try_pre_increment (y, SET_DEST (x), amount))
     {
       /* We have found a suitable auto-increment
         and already changed insn Y to do it.
@@ -2751,14 +2938,14 @@ try_pre_increment (insn, reg, amount)
 
   /* 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))
+                        gen_rtx_fmt_e (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));
+  REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC, reg, REG_NOTES (insn));
   return 1;
 }
 
@@ -2773,7 +2960,7 @@ try_pre_increment (insn, reg, amount)
    If REG appears more than once, or is used other than in such an address,
    return (rtx)1.  */
 
-static rtx
+rtx
 find_use_as_address (x, reg, plusconst)
      register rtx x;
      rtx reg;
@@ -2852,6 +3039,11 @@ dump_flow_info (file)
                 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_SETS (i))
+         fprintf (file, "; set %d time%s", REG_N_SETS (i),
+                  (REG_N_SETS (i) == 1) ? "" : "s");
+       if (REG_USERVAR_P (regno_reg_rtx[i]))
+         fprintf (file, "; user var");
        if (REG_N_DEATHS (i) != 1)
          fprintf (file, "; dies in %d places", REG_N_DEATHS (i));
        if (REG_N_CALLS_CROSSED (i) == 1)
@@ -2912,3 +3104,881 @@ dump_flow_info (file)
     }
   fprintf (file, "\n");
 }
+
+\f
+/* Like print_rtl, but also print out live information for the start of each
+   basic block.  */
+
+void
+print_rtl_with_bb (outf, rtx_first)
+     FILE *outf;
+     rtx rtx_first;
+{
+  register rtx tmp_rtx;
+
+  if (rtx_first == 0)
+    fprintf (outf, "(nil)\n");
+
+  else
+    {
+      int i, bb;
+      enum bb_state { NOT_IN_BB, IN_ONE_BB, IN_MULTIPLE_BB };
+      int max_uid = get_max_uid ();
+      int *start = (int *) alloca (max_uid * sizeof (int));
+      int *end = (int *) alloca (max_uid * sizeof (int));
+      char *in_bb_p = (char *) alloca (max_uid * sizeof (enum bb_state));
+
+      for (i = 0; i < max_uid; i++)
+       {
+         start[i] = end[i] = -1;
+         in_bb_p[i] = NOT_IN_BB;
+       }
+
+      for (i = n_basic_blocks-1; i >= 0; i--)
+       {
+         rtx x;
+         start[INSN_UID (basic_block_head[i])] = i;
+         end[INSN_UID (basic_block_end[i])] = i;
+         for (x = basic_block_head[i]; x != NULL_RTX; x = NEXT_INSN (x))
+           {
+             in_bb_p[ INSN_UID(x)]
+               = (in_bb_p[ INSN_UID(x)] == NOT_IN_BB)
+                ? IN_ONE_BB : IN_MULTIPLE_BB;
+             if (x == basic_block_end[i])
+               break;
+           }
+       }
+
+      for (tmp_rtx = rtx_first; NULL != tmp_rtx; tmp_rtx = NEXT_INSN (tmp_rtx))
+       {
+         if ((bb = start[INSN_UID (tmp_rtx)]) >= 0)
+           {
+             fprintf (outf, ";; Start of basic block %d, registers live:",
+                      bb);
+
+             EXECUTE_IF_SET_IN_REG_SET (basic_block_live_at_start[bb], 0, i,
+                                        {
+                                          fprintf (outf, " %d", i);
+                                          if (i < FIRST_PSEUDO_REGISTER)
+                                            fprintf (outf, " [%s]",
+                                                     reg_names[i]);
+                                        });
+             putc ('\n', outf);
+           }
+
+         if (in_bb_p[ INSN_UID(tmp_rtx)] == NOT_IN_BB
+             && GET_CODE (tmp_rtx) != NOTE
+             && GET_CODE (tmp_rtx) != BARRIER)
+           fprintf (outf, ";; Insn is not within a basic block\n");
+         else if (in_bb_p[ INSN_UID(tmp_rtx)] == IN_MULTIPLE_BB)
+           fprintf (outf, ";; Insn is in multiple basic blocks\n");
+
+         print_rtl_single (outf, tmp_rtx);
+
+         if ((bb = end[INSN_UID (tmp_rtx)]) >= 0)
+           fprintf (outf, ";; End of basic block %d\n", bb);
+
+         putc ('\n', outf);
+       }
+    }
+}
+
+\f
+/* Integer list support.  */
+
+/* Allocate a node from list *HEAD_PTR.  */
+
+static int_list_ptr
+alloc_int_list_node (head_ptr)
+     int_list_block **head_ptr;
+{
+  struct int_list_block *first_blk = *head_ptr;
+
+  if (first_blk == NULL || first_blk->nodes_left <= 0)
+    {
+      first_blk = (struct int_list_block *) xmalloc (sizeof (struct int_list_block));
+      first_blk->nodes_left = INT_LIST_NODES_IN_BLK;
+      first_blk->next = *head_ptr;
+      *head_ptr = first_blk;
+    }
+
+  first_blk->nodes_left--;
+  return &first_blk->nodes[first_blk->nodes_left];
+}
+
+/* Pointer to head of predecessor/successor block list.  */
+static int_list_block *pred_int_list_blocks;
+
+/* Add a new node to integer list LIST with value VAL.
+   LIST is a pointer to a list object to allow for different implementations.
+   If *LIST is initially NULL, the list is empty.
+   The caller must not care whether the element is added to the front or
+   to the end of the list (to allow for different implementations).  */
+
+static int_list_ptr
+add_int_list_node (blk_list, list, val)
+     int_list_block **blk_list;
+     int_list **list;
+     int val;
+{
+  int_list_ptr p = alloc_int_list_node (blk_list);
+
+  p->val = val;
+  p->next = *list;
+  *list = p;
+  return p;
+}
+
+/* Free the blocks of lists at BLK_LIST.  */
+
+void
+free_int_list (blk_list)
+     int_list_block **blk_list;
+{
+  int_list_block *p, *next;
+
+  for (p = *blk_list; p != NULL; p = next)
+    {
+      next = p->next;
+      free (p);
+    }
+
+  /* Mark list as empty for the next function we compile.  */
+  *blk_list = NULL;
+}
+\f
+/* Predecessor/successor computation.  */
+
+/* Mark PRED_BB a precessor of SUCC_BB,
+   and conversely SUCC_BB a successor of PRED_BB.  */
+
+static void
+add_pred_succ (pred_bb, succ_bb, s_preds, s_succs, num_preds, num_succs)
+     int pred_bb;
+     int succ_bb;
+     int_list_ptr *s_preds;
+     int_list_ptr *s_succs;
+     int *num_preds;
+     int *num_succs;
+{
+  if (succ_bb != EXIT_BLOCK)
+    {
+      add_int_list_node (&pred_int_list_blocks, &s_preds[succ_bb], pred_bb);
+      num_preds[succ_bb]++;
+    }
+  if (pred_bb != ENTRY_BLOCK)
+    {
+      add_int_list_node (&pred_int_list_blocks, &s_succs[pred_bb], succ_bb);
+      num_succs[pred_bb]++;
+    }
+}
+
+/* Compute the predecessors and successors for each block.  */
+void
+compute_preds_succs (s_preds, s_succs, num_preds, num_succs)
+     int_list_ptr *s_preds;
+     int_list_ptr *s_succs;
+     int *num_preds;
+     int *num_succs;
+{
+  int bb, clear_local_bb_vars = 0;
+
+  bzero ((char *) s_preds, n_basic_blocks * sizeof (int_list_ptr));
+  bzero ((char *) s_succs, n_basic_blocks * sizeof (int_list_ptr));
+  bzero ((char *) num_preds, n_basic_blocks * sizeof (int));
+  bzero ((char *) num_succs, n_basic_blocks * sizeof (int));
+
+  /* This routine can be called after life analysis; in that case
+     basic_block_drops_in and uid_block_number will not be available
+     and we must recompute their values.  */
+  if (basic_block_drops_in == NULL || uid_block_number == NULL)
+    {
+      clear_local_bb_vars = 1;
+      basic_block_drops_in = (char *) alloca (n_basic_blocks);
+      uid_block_number = (int *) alloca ((get_max_uid () + 1) * sizeof (int));
+
+      bzero ((char *) basic_block_drops_in, n_basic_blocks * sizeof (char));
+      bzero ((char *) uid_block_number, n_basic_blocks * sizeof (int));
+
+      /* Scan each basic block setting basic_block_drops_in and
+        uid_block_number as needed.  */
+      for (bb = 0; bb < n_basic_blocks; bb++)
+       {
+         rtx insn, stop_insn;
+
+         if (bb == 0)
+           stop_insn = NULL_RTX;
+         else
+           stop_insn = basic_block_end[bb-1];
+
+         /* Look backwards from the start of this block.  Stop if we
+            hit the start of the function or the end of a previous
+            block.  Don't walk backwards through blocks that are just
+            deleted insns!  */
+         for (insn = PREV_INSN (basic_block_head[bb]);
+              insn && insn != stop_insn && GET_CODE (insn) == NOTE;
+              insn = PREV_INSN (insn))
+           ;
+
+         /* Never set basic_block_drops_in for the first block.  It is
+            implicit.
+
+            If we stopped on anything other than a BARRIER, then this
+            block drops in.  */
+         if (bb != 0)
+           basic_block_drops_in[bb] = (insn ? GET_CODE (insn) != BARRIER : 1);
+
+         insn = basic_block_head[bb];
+         while (insn)
+           {
+             BLOCK_NUM (insn) = bb;
+             if (insn == basic_block_end[bb])
+               break;
+             insn = NEXT_INSN (insn);
+           }
+       }
+    }
+      
+  for (bb = 0; bb < n_basic_blocks; bb++)
+    {
+      rtx head;
+      rtx jump;
+
+      head = BLOCK_HEAD (bb);
+
+      if (GET_CODE (head) == CODE_LABEL)
+       for (jump = LABEL_REFS (head);
+            jump != head;
+            jump = LABEL_NEXTREF (jump))
+         {
+           if (! INSN_DELETED_P (CONTAINING_INSN (jump))
+               && (GET_CODE (CONTAINING_INSN (jump)) != NOTE
+                   || (NOTE_LINE_NUMBER (CONTAINING_INSN (jump))
+                       != NOTE_INSN_DELETED)))
+             add_pred_succ (BLOCK_NUM (CONTAINING_INSN (jump)), bb,
+                            s_preds, s_succs, num_preds, num_succs);
+         }
+
+      jump = BLOCK_END (bb);
+      /* If this is a RETURN insn or a conditional jump in the last
+        basic block, or a non-jump insn in the last basic block, then
+        this block reaches the exit block.  */
+      if ((GET_CODE (jump) == JUMP_INSN && GET_CODE (PATTERN (jump)) == RETURN)
+         || (((GET_CODE (jump) == JUMP_INSN
+               && condjump_p (jump) && !simplejump_p (jump))
+              || GET_CODE (jump) != JUMP_INSN)
+             && (bb == n_basic_blocks - 1)))
+       add_pred_succ (bb, EXIT_BLOCK, s_preds, s_succs, num_preds, num_succs);
+
+      if (basic_block_drops_in[bb])
+       add_pred_succ (bb - 1, bb, s_preds, s_succs, num_preds, num_succs);
+    }
+
+  add_pred_succ (ENTRY_BLOCK, 0, s_preds, s_succs, num_preds, num_succs);
+
+
+  /* If we allocated any variables in temporary storage, clear out the
+     pointer to the local storage to avoid dangling pointers.  */
+  if (clear_local_bb_vars)
+    {
+      basic_block_drops_in = NULL;
+      uid_block_number = NULL;
+    
+    }
+}
+
+void
+dump_bb_data (file, preds, succs)
+     FILE *file;
+     int_list_ptr *preds;
+     int_list_ptr *succs;
+{
+  int bb;
+  int_list_ptr p;
+
+  fprintf (file, "BB data\n\n");
+  for (bb = 0; bb < n_basic_blocks; bb++)
+    {
+      fprintf (file, "BB %d, start %d, end %d\n", bb,
+              INSN_UID (BLOCK_HEAD (bb)), INSN_UID (BLOCK_END (bb)));
+      fprintf (file, "  preds:");
+      for (p = preds[bb]; p != NULL; p = p->next)
+       {
+         int pred_bb = INT_LIST_VAL (p);
+         if (pred_bb == ENTRY_BLOCK)
+           fprintf (file, " entry");
+         else
+           fprintf (file, " %d", pred_bb);
+       }
+      fprintf (file, "\n");
+      fprintf (file, "  succs:");
+      for (p = succs[bb]; p != NULL; p = p->next)
+       {
+         int succ_bb = INT_LIST_VAL (p);
+         if (succ_bb == EXIT_BLOCK)
+           fprintf (file, " exit");
+         else
+           fprintf (file, " %d", succ_bb);
+       }
+      fprintf (file, "\n");
+    }
+  fprintf (file, "\n");
+}
+
+void
+dump_sbitmap (file, bmap)
+     FILE *file;
+     sbitmap bmap;
+{
+  int i,j,n;
+  int set_size = bmap->size;
+  int total_bits = bmap->n_bits;
+
+  fprintf (file, "  ");
+  for (i = n = 0; i < set_size && n < total_bits; i++)
+    {
+      for (j = 0; j < SBITMAP_ELT_BITS && n < total_bits; j++, n++)
+       {
+         if (n != 0 && n % 10 == 0)
+           fprintf (file, " ");
+         fprintf (file, "%d", (bmap->elms[i] & (1L << j)) != 0);
+       }
+    }
+  fprintf (file, "\n");
+}
+
+void
+dump_sbitmap_vector (file, title, subtitle, bmaps, n_maps)
+     FILE *file;
+     char *title, *subtitle;
+     sbitmap *bmaps;
+     int n_maps;
+{
+  int bb;
+
+  fprintf (file, "%s\n", title);
+  for (bb = 0; bb < n_maps; bb++)
+    {
+      fprintf (file, "%s %d\n", subtitle, bb);
+      dump_sbitmap (file, bmaps[bb]);
+    }
+  fprintf (file, "\n");
+}
+
+/* Free basic block data storage.  */
+
+void
+free_bb_mem ()
+{
+  free_int_list (&pred_int_list_blocks);
+}
+\f
+/* Bitmap manipulation routines.  */
+
+/* Allocate a simple bitmap of N_ELMS bits.  */
+
+sbitmap
+sbitmap_alloc (n_elms)
+     int n_elms;
+{
+  int bytes, size, amt;
+  sbitmap bmap;
+
+  size = SBITMAP_SET_SIZE (n_elms);
+  bytes = size * sizeof (SBITMAP_ELT_TYPE);
+  amt = (sizeof (struct simple_bitmap_def)
+        + bytes - sizeof (SBITMAP_ELT_TYPE));
+  bmap = (sbitmap) xmalloc (amt);
+  bmap->n_bits = n_elms;
+  bmap->size = size;
+  bmap->bytes = bytes;
+  return bmap;
+}
+
+/* Allocate a vector of N_VECS bitmaps of N_ELMS bits.  */
+
+sbitmap *
+sbitmap_vector_alloc (n_vecs, n_elms)
+     int n_vecs, n_elms;
+{
+  int i, bytes, offset, elm_bytes, size, amt, vector_bytes;
+  sbitmap *bitmap_vector;
+
+  size = SBITMAP_SET_SIZE (n_elms);
+  bytes = size * sizeof (SBITMAP_ELT_TYPE);
+  elm_bytes = (sizeof (struct simple_bitmap_def)
+              + bytes - sizeof (SBITMAP_ELT_TYPE));
+  vector_bytes = n_vecs * sizeof (sbitmap *);
+
+  /* Round up `vector_bytes' to account for the alignment requirements
+     of an sbitmap.  One could allocate the vector-table and set of sbitmaps
+     separately, but that requires maintaining two pointers or creating
+     a cover struct to hold both pointers (so our result is still just
+     one pointer).  Neither is a bad idea, but this is simpler for now.  */
+  {
+    /* Based on DEFAULT_ALIGNMENT computation in obstack.c.  */
+    struct { char x; SBITMAP_ELT_TYPE y; } align;
+    int alignment = (char *) & align.y - & align.x;
+    vector_bytes = (vector_bytes + alignment - 1) & ~ (alignment - 1);
+  }
+
+  amt = vector_bytes + (n_vecs * elm_bytes);
+  bitmap_vector = (sbitmap *) xmalloc (amt);
+
+  for (i = 0, offset = vector_bytes;
+       i < n_vecs;
+       i++, offset += elm_bytes)
+    {
+      sbitmap b = (sbitmap) ((char *) bitmap_vector + offset);
+      bitmap_vector[i] = b;
+      b->n_bits = n_elms;
+      b->size = size;
+      b->bytes = bytes;
+    }
+
+  return bitmap_vector;
+}
+
+/* Copy sbitmap SRC to DST.  */
+
+void
+sbitmap_copy (dst, src)
+     sbitmap dst, src;
+{
+  int i;
+  sbitmap_ptr d,s;
+
+  s = src->elms;
+  d = dst->elms;
+  for (i = 0; i < dst->size; i++)
+    *d++ = *s++;
+}
+
+/* Zero all elements in a bitmap.  */
+
+void
+sbitmap_zero (bmap)
+     sbitmap bmap;
+{
+  bzero ((char *) bmap->elms, bmap->bytes);
+}
+
+/* Set to ones all elements in a bitmap.  */
+
+void
+sbitmap_ones (bmap)
+     sbitmap bmap;
+{
+  memset (bmap->elms, -1, bmap->bytes);
+}
+
+/* Zero a vector of N_VECS bitmaps.  */
+
+void
+sbitmap_vector_zero (bmap, n_vecs)
+     sbitmap *bmap;
+     int n_vecs;
+{
+  int i;
+
+  for (i = 0; i < n_vecs; i++)
+    sbitmap_zero (bmap[i]);
+}
+
+/* Set to ones a vector of N_VECS bitmaps.  */
+
+void
+sbitmap_vector_ones (bmap, n_vecs)
+     sbitmap *bmap;
+     int n_vecs;
+{
+  int i;
+
+  for (i = 0; i < n_vecs; i++)
+    sbitmap_ones (bmap[i]);
+}
+
+/* Set DST to be A union (B - C).
+   DST = A | (B & ~C).
+   Return non-zero if any change is made.  */
+
+int
+sbitmap_union_of_diff (dst, a, b, c)
+     sbitmap dst, a, b, c;
+{
+  int i,changed;
+  sbitmap_ptr dstp, ap, bp, cp;
+
+  changed = 0;
+  dstp = dst->elms;
+  ap = a->elms;
+  bp = b->elms;
+  cp = c->elms;
+  for (i = 0; i < dst->size; i++)
+    {
+      SBITMAP_ELT_TYPE tmp = *ap | (*bp & ~*cp);
+      if (*dstp != tmp)
+       changed = 1;
+      *dstp = tmp;
+      dstp++; ap++; bp++; cp++;
+    }
+  return changed;
+}
+
+/* Set bitmap DST to the bitwise negation of the bitmap SRC.  */
+
+void
+sbitmap_not (dst, src)
+     sbitmap dst, src;
+{
+  int i;
+  sbitmap_ptr dstp, ap;
+
+  dstp = dst->elms;
+  ap = src->elms;
+  for (i = 0; i < dst->size; i++)
+    {
+      SBITMAP_ELT_TYPE tmp = ~(*ap);
+      *dstp = tmp;
+      dstp++; ap++;
+    }
+}
+
+/* Set the bits in DST to be the difference between the bits
+   in A and the bits in B. i.e. dst = a - b.
+   The - operator is implemented as a & (~b).  */
+
+void
+sbitmap_difference (dst, a, b)
+     sbitmap dst, a, b;
+{
+  int i;
+  sbitmap_ptr dstp, ap, bp;
+
+  dstp = dst->elms;
+  ap = a->elms;
+  bp = b->elms;
+  for (i = 0; i < dst->size; i++)
+    *dstp++ = *ap++ & (~*bp++);
+}
+
+/* Set DST to be (A and B)).
+   Return non-zero if any change is made.  */
+
+int
+sbitmap_a_and_b (dst, a, b)
+     sbitmap dst, a, b;
+{
+  int i,changed;
+  sbitmap_ptr dstp, ap, bp;
+
+  changed = 0;
+  dstp = dst->elms;
+  ap = a->elms;
+  bp = b->elms;
+  for (i = 0; i < dst->size; i++)
+    {
+      SBITMAP_ELT_TYPE tmp = *ap & *bp;
+      if (*dstp != tmp)
+       changed = 1;
+      *dstp = tmp;
+      dstp++; ap++; bp++;
+    }
+  return changed;
+}
+/* Set DST to be (A or B)).
+   Return non-zero if any change is made.  */
+
+int
+sbitmap_a_or_b (dst, a, b)
+     sbitmap dst, a, b;
+{
+  int i,changed;
+  sbitmap_ptr dstp, ap, bp;
+
+  changed = 0;
+  dstp = dst->elms;
+  ap = a->elms;
+  bp = b->elms;
+  for (i = 0; i < dst->size; i++)
+    {
+      SBITMAP_ELT_TYPE tmp = *ap | *bp;
+      if (*dstp != tmp)
+       changed = 1;
+      *dstp = tmp;
+      dstp++; ap++; bp++;
+    }
+  return changed;
+}
+
+/* Set DST to be (A or (B and C)).
+   Return non-zero if any change is made.  */
+
+int
+sbitmap_a_or_b_and_c (dst, a, b, c)
+     sbitmap dst, a, b, c;
+{
+  int i,changed;
+  sbitmap_ptr dstp, ap, bp, cp;
+
+  changed = 0;
+  dstp = dst->elms;
+  ap = a->elms;
+  bp = b->elms;
+  cp = c->elms;
+  for (i = 0; i < dst->size; i++)
+    {
+      SBITMAP_ELT_TYPE tmp = *ap | (*bp & *cp);
+      if (*dstp != tmp)
+       changed = 1;
+      *dstp = tmp;
+      dstp++; ap++; bp++; cp++;
+    }
+  return changed;
+}
+
+/* Set DST to be (A ann (B or C)).
+   Return non-zero if any change is made.  */
+
+int
+sbitmap_a_and_b_or_c (dst, a, b, c)
+     sbitmap dst, a, b, c;
+{
+  int i,changed;
+  sbitmap_ptr dstp, ap, bp, cp;
+
+  changed = 0;
+  dstp = dst->elms;
+  ap = a->elms;
+  bp = b->elms;
+  cp = c->elms;
+  for (i = 0; i < dst->size; i++)
+    {
+      SBITMAP_ELT_TYPE tmp = *ap & (*bp | *cp);
+      if (*dstp != tmp)
+       changed = 1;
+      *dstp = tmp;
+      dstp++; ap++; bp++; cp++;
+    }
+  return changed;
+}
+
+/* Set the bitmap DST to the intersection of SRC of all predecessors or
+   successors of block number BB (PRED_SUCC says which).  */
+
+void
+sbitmap_intersect_of_predsucc (dst, src, bb, pred_succ)
+     sbitmap dst;
+     sbitmap *src;
+     int bb;
+     int_list_ptr *pred_succ;
+{
+  int_list_ptr ps;
+  int ps_bb;
+  int set_size = dst->size;
+
+  ps = pred_succ[bb];
+
+  /* It is possible that there are no predecessors(/successors).
+     This can happen for example in unreachable code.  */
+
+  if (ps == NULL)
+    {
+      /* In APL-speak this is the `and' reduction of the empty set and thus
+        the result is the identity for `and'.  */
+      sbitmap_ones (dst);
+      return;
+    }
+
+  /* Set result to first predecessor/successor.  */
+
+  for ( ; ps != NULL; ps = ps->next)
+    {
+      ps_bb = INT_LIST_VAL (ps);
+      if (ps_bb == ENTRY_BLOCK || ps_bb == EXIT_BLOCK)
+       continue;
+      sbitmap_copy (dst, src[ps_bb]);
+      /* Break out since we're only doing first predecessor.  */
+      break;
+    }
+  if (ps == NULL)
+    return;
+
+  /* Now do the remaining predecessors/successors.  */
+
+  for (ps = ps->next; ps != NULL; ps = ps->next)
+    {
+      int i;
+      sbitmap_ptr p,r;
+
+      ps_bb = INT_LIST_VAL (ps);
+      if (ps_bb == ENTRY_BLOCK || ps_bb == EXIT_BLOCK)
+       continue;
+
+      p = src[ps_bb]->elms;
+      r = dst->elms;
+
+      for (i = 0; i < set_size; i++)
+       *r++ &= *p++;
+    }
+}
+
+/* Set the bitmap DST to the intersection of SRC of all predecessors
+   of block number BB.  */
+
+void
+sbitmap_intersect_of_predecessors (dst, src, bb, s_preds)
+     sbitmap dst;
+     sbitmap *src;
+     int bb;
+     int_list_ptr *s_preds;
+{
+  sbitmap_intersect_of_predsucc (dst, src, bb, s_preds);
+}
+
+/* Set the bitmap DST to the intersection of SRC of all successors
+   of block number BB.  */
+
+void
+sbitmap_intersect_of_successors (dst, src, bb, s_succs)
+     sbitmap dst;
+     sbitmap *src;
+     int bb;
+     int_list_ptr *s_succs;
+{
+  sbitmap_intersect_of_predsucc (dst, src, bb, s_succs);
+}
+
+/* Set the bitmap DST to the union of SRC of all predecessors/successors of
+   block number BB.  */
+
+void
+sbitmap_union_of_predsucc (dst, src, bb, pred_succ)
+     sbitmap dst;
+     sbitmap *src;
+     int bb;
+     int_list_ptr *pred_succ;
+{
+  int_list_ptr ps;
+  int ps_bb;
+  int set_size = dst->size;
+
+  ps = pred_succ[bb];
+
+  /* It is possible that there are no predecessors(/successors).
+     This can happen for example in unreachable code.  */
+
+  if (ps == NULL)
+    {
+      /* In APL-speak this is the `or' reduction of the empty set and thus
+        the result is the identity for `or'.  */
+      sbitmap_zero (dst);
+      return;
+    }
+
+  /* Set result to first predecessor/successor.  */
+
+  for ( ; ps != NULL; ps = ps->next)
+    {
+      ps_bb = INT_LIST_VAL (ps);
+      if (ps_bb == ENTRY_BLOCK || ps_bb == EXIT_BLOCK)
+       continue;
+      sbitmap_copy (dst, src[ps_bb]);
+      /* Break out since we're only doing first predecessor.  */
+      break;
+    }
+  if (ps == NULL)
+    return;
+
+  /* Now do the remaining predecessors/successors.  */
+
+  for (ps = ps->next; ps != NULL; ps = ps->next)
+    {
+      int i;
+      sbitmap_ptr p,r;
+
+      ps_bb = INT_LIST_VAL (ps);
+      if (ps_bb == ENTRY_BLOCK || ps_bb == EXIT_BLOCK)
+       continue;
+
+      p = src[ps_bb]->elms;
+      r = dst->elms;
+
+      for (i = 0; i < set_size; i++)
+       *r++ |= *p++;
+    }
+}
+
+/* Set the bitmap DST to the union of SRC of all predecessors of
+   block number BB.  */
+
+void
+sbitmap_union_of_predecessors (dst, src, bb, s_preds)
+     sbitmap dst;
+     sbitmap *src;
+     int bb;
+     int_list_ptr *s_preds;
+{
+  sbitmap_union_of_predsucc (dst, src, bb, s_preds);
+}
+
+/* Set the bitmap DST to the union of SRC of all predecessors of
+   block number BB.  */
+
+void
+sbitmap_union_of_successors (dst, src, bb, s_succ)
+     sbitmap dst;
+     sbitmap *src;
+     int bb;
+     int_list_ptr *s_succ;
+{
+  sbitmap_union_of_predsucc (dst, src, bb, s_succ);
+}
+
+/* Compute dominator relationships.  */
+void
+compute_dominators (dominators, post_dominators, s_preds, s_succs)
+     sbitmap *dominators;
+     sbitmap *post_dominators;
+     int_list_ptr *s_preds;
+     int_list_ptr *s_succs;
+{
+  int bb, changed, passes;
+  sbitmap *temp_bitmap;
+
+  temp_bitmap = sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks);
+  sbitmap_vector_ones (dominators, n_basic_blocks);
+  sbitmap_vector_ones (post_dominators, n_basic_blocks);
+  sbitmap_vector_zero (temp_bitmap, n_basic_blocks);
+
+  sbitmap_zero (dominators[0]);
+  SET_BIT (dominators[0], 0);
+
+  sbitmap_zero (post_dominators[n_basic_blocks-1]);
+  SET_BIT (post_dominators[n_basic_blocks-1], 0);
+
+  passes = 0;
+  changed = 1;
+  while (changed)
+    {
+      changed = 0;
+      for (bb = 1; bb < n_basic_blocks; bb++)
+       {
+         sbitmap_intersect_of_predecessors (temp_bitmap[bb], dominators,
+                                            bb, s_preds);
+         SET_BIT (temp_bitmap[bb], bb);
+         changed |= sbitmap_a_and_b (dominators[bb],
+                                     dominators[bb],
+                                     temp_bitmap[bb]);
+         sbitmap_intersect_of_successors (temp_bitmap[bb], post_dominators,
+                                          bb, s_succs);
+         SET_BIT (temp_bitmap[bb], bb);
+         changed |= sbitmap_a_and_b (post_dominators[bb],
+                                     post_dominators[bb],
+                                     temp_bitmap[bb]);
+       }
+      passes++;
+    }
+
+  free (temp_bitmap);
+}