]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/cse.c
Merge from trunk.
[thirdparty/gcc.git] / gcc / cse.c
index a078329ac553c18c232a8af5c49ea3bf308fdc41..15e582cd223a581c70c9bb0e20e695ee44af95a2 100644 (file)
--- a/gcc/cse.c
+++ b/gcc/cse.c
@@ -1,7 +1,5 @@
 /* Common subexpression elimination for GNU compiler.
-   Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998
-   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-   2011 Free Software Foundation, Inc.
+   Copyright (C) 1987-2013 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -35,9 +33,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "expr.h"
 #include "diagnostic-core.h"
 #include "toplev.h"
-#include "output.h"
 #include "ggc.h"
-#include "timevar.h"
 #include "except.h"
 #include "target.h"
 #include "params.h"
@@ -45,6 +41,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pass.h"
 #include "df.h"
 #include "dbgcnt.h"
+#include "pointer-set.h"
 
 /* The basic idea of common subexpression elimination is to go
    through the code, keeping a record of expressions that would
@@ -471,12 +468,12 @@ struct table_elt
    a cost of 2.  Aside from these special cases, call `rtx_cost'.  */
 
 #define CHEAP_REGNO(N)                                                 \
-  (REGNO_PTR_FRAME_P(N)                                                        \
+  (REGNO_PTR_FRAME_P (N)                                               \
    || (HARD_REGISTER_NUM_P (N)                                         \
        && FIXED_REGNO_P (N) && REGNO_REG_CLASS (N) != NO_REGS))
 
-#define COST(X) (REG_P (X) ? 0 : notreg_cost (X, SET))
-#define COST_IN(X,OUTER) (REG_P (X) ? 0 : notreg_cost (X, OUTER))
+#define COST(X) (REG_P (X) ? 0 : notreg_cost (X, SET, 1))
+#define COST_IN(X, OUTER, OPNO) (REG_P (X) ? 0 : notreg_cost (X, OUTER, OPNO))
 
 /* Get the number of times this register has been updated in this
    basic block.  */
@@ -552,7 +549,7 @@ static bitmap cse_ebb_live_in, cse_ebb_live_out;
 static sbitmap cse_visited_basic_blocks;
 
 static bool fixed_base_plus_p (rtx x);
-static int notreg_cost (rtx, enum rtx_code);
+static int notreg_cost (rtx, enum rtx_code, int);
 static int approx_reg_cost_1 (rtx *, void *);
 static int approx_reg_cost (rtx);
 static int preferable (int, int, int, int);
@@ -573,7 +570,6 @@ static struct table_elt *insert (rtx, struct table_elt *, unsigned,
                                 enum machine_mode);
 static void merge_equiv_classes (struct table_elt *, struct table_elt *);
 static void invalidate (rtx, enum machine_mode);
-static bool cse_rtx_varies_p (const_rtx, bool);
 static void remove_invalid_refs (unsigned int);
 static void remove_invalid_subreg_refs (unsigned int, unsigned int,
                                        enum machine_mode);
@@ -598,9 +594,9 @@ static void record_jump_cond (enum rtx_code, enum machine_mode, rtx, rtx,
 static void cse_insn (rtx);
 static void cse_prescan_path (struct cse_basic_block_data *);
 static void invalidate_from_clobbers (rtx);
+static void invalidate_from_sets_and_clobbers (rtx);
 static rtx cse_process_notes (rtx, rtx, bool *);
 static void cse_extended_basic_block (struct cse_basic_block_data *);
-static void count_reg_usage (rtx, int *, rtx, int);
 static int check_for_label_ref (rtx *, void *);
 extern void dump_class (struct table_elt*);
 static void get_cse_reg_info_1 (unsigned int regno);
@@ -622,9 +618,7 @@ static enum machine_mode cse_cc_succs (basic_block, basic_block, rtx, rtx,
 
 static const struct rtl_hooks cse_rtl_hooks = RTL_HOOKS_INITIALIZER;
 \f
-/* Nonzero if X has the form (PLUS frame-pointer integer).  We check for
-   virtual regs here because the simplify_*_operation routines are called
-   by integrate.c, which is called before virtual register instantiation.  */
+/* Nonzero if X has the form (PLUS frame-pointer integer).  */
 
 static bool
 fixed_base_plus_p (rtx x)
@@ -636,9 +630,6 @@ fixed_base_plus_p (rtx x)
        return true;
       if (x == arg_pointer_rtx && fixed_regs[ARG_POINTER_REGNUM])
        return true;
-      if (REGNO (x) >= FIRST_VIRTUAL_REGISTER
-         && REGNO (x) <= LAST_VIRTUAL_REGISTER)
-       return true;
       return false;
 
     case PLUS:
@@ -653,7 +644,7 @@ fixed_base_plus_p (rtx x)
 
 /* Dump the expressions in the equivalence class indicated by CLASSP.
    This function is used only for debugging.  */
-void
+DEBUG_FUNCTION void
 dump_class (struct table_elt *classp)
 {
   struct table_elt *elt;
@@ -752,7 +743,7 @@ preferable (int cost_a, int regcost_a, int cost_b, int regcost_b)
    from COST macro to keep it simple.  */
 
 static int
-notreg_cost (rtx x, enum rtx_code outer)
+notreg_cost (rtx x, enum rtx_code outer, int opno)
 {
   return ((GET_CODE (x) == SUBREG
           && REG_P (SUBREG_REG (x))
@@ -764,7 +755,7 @@ notreg_cost (rtx x, enum rtx_code outer)
           && TRULY_NOOP_TRUNCATION_MODES_P (GET_MODE (x),
                                             GET_MODE (SUBREG_REG (x))))
          ? 0
-         : rtx_cost (x, outer, optimize_this_for_speed_p) * 2);
+         : rtx_cost (x, outer, opno, optimize_this_for_speed_p) * 2);
 }
 
 \f
@@ -1258,7 +1249,7 @@ insert_const_anchor (HOST_WIDE_INT anchor, rtx reg, HOST_WIDE_INT offs,
   if (!elt)
     elt = insert (anchor_exp, NULL, hash, mode);
 
-  exp = plus_constant (reg, offs);
+  exp = plus_constant (mode, reg, offs);
   /* REG has just been inserted and the hash codes recomputed.  */
   mention_regs (exp);
   hash = HASH (exp, mode);
@@ -1333,7 +1324,7 @@ find_reg_offset_for_const (struct table_elt *anchor_elt, HOST_WIDE_INT offs,
          if (!REG_P (elt->exp) && !exp_equiv_p (elt->exp, elt->exp, 1, false))
            continue;
 
-         x = plus_constant (elt->exp, offs);
+         x = plus_constant (GET_MODE (elt->exp), elt->exp, offs);
          if (REG_P (x)
              || (GET_CODE (x) == PLUS
                  && IN_RANGE (INTVAL (XEXP (x, 1)),
@@ -1363,6 +1354,11 @@ try_const_anchors (rtx src_const, enum machine_mode mode)
   rtx lower_exp = NULL_RTX, upper_exp = NULL_RTX;
   unsigned lower_old, upper_old;
 
+  /* CONST_INT is used for CC modes, but we should leave those alone.  */
+  if (GET_MODE_CLASS (mode) == MODE_CC)
+    return NULL_RTX;
+
+  gcc_assert (SCALAR_INT_MODE_P (mode));
   if (!compute_const_anchors (src_const, &lower_base, &lower_offs,
                              &upper_base, &upper_offs))
     return NULL_RTX;
@@ -1637,8 +1633,10 @@ insert_with_costs (rtx x, struct table_elt *classp, unsigned int hash,
          /* Put it after the last element cheaper than X.  */
          struct table_elt *p, *next;
 
-         for (p = classp; (next = p->next_same_value) && CHEAPER (next, elt);
-              p = next);
+         for (p = classp;
+              (next = p->next_same_value) && CHEAPER (next, elt);
+              p = next)
+           ;
 
          /* Put it after P and before NEXT.  */
          elt->next_same_value = next;
@@ -1831,7 +1829,7 @@ flush_hash_table (void)
       }
 }
 \f
-/* Function called for each rtx to check whether true dependence exist.  */
+/* Function called for each rtx to check whether an anti dependence exist.  */
 struct check_dependence_data
 {
   enum machine_mode mode;
@@ -1844,8 +1842,7 @@ check_dependence (rtx *x, void *data)
 {
   struct check_dependence_data *d = (struct check_dependence_data *) data;
   if (*x && MEM_P (*x))
-    return canon_true_dependence (d->exp, d->mode, d->addr, *x, NULL_RTX,
-                                 cse_rtx_varies_p);
+    return canon_anti_dependence (*x, true, d->exp, d->mode, d->addr);
   else
     return 0;
 }
@@ -2102,24 +2099,22 @@ invalidate_for_call (void)
   unsigned hash;
   struct table_elt *p, *next;
   int in_table = 0;
+  hard_reg_set_iterator hrsi;
 
   /* Go through all the hard registers.  For each that is clobbered in
      a CALL_INSN, remove the register from quantity chains and update
      reg_tick if defined.  Also see if any of these registers is currently
      in the table.  */
-
-  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-    if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
-      {
-       delete_reg_equiv (regno);
-       if (REG_TICK (regno) >= 0)
-         {
-           REG_TICK (regno)++;
-           SUBREG_TICKED (regno) = -1;
-         }
-
-       in_table |= (TEST_HARD_REG_BIT (hard_regs_in_table, regno) != 0);
-      }
+  EXECUTE_IF_SET_IN_HARD_REG_SET (regs_invalidated_by_call, 0, regno, hrsi)
+    {
+      delete_reg_equiv (regno);
+      if (REG_TICK (regno) >= 0)
+       {
+         REG_TICK (regno)++;
+         SUBREG_TICKED (regno) = -1;
+       }
+      in_table |= (TEST_HARD_REG_BIT (hard_regs_in_table, regno) != 0);
+    }
 
   /* In the case where we have no call-clobbered hard registers in the
      table, we are done.  Otherwise, scan the table and remove any
@@ -2217,7 +2212,7 @@ use_related_value (rtx x, struct table_elt *elt)
 
   offset = (get_integer_term (x) - get_integer_term (p->exp));
   /* Note: OFFSET may be 0 if P->xexp and X are related by commutativity.  */
-  return plus_constant (q->exp, offset);
+  return plus_constant (q->mode, q->exp, offset);
 }
 \f
 
@@ -2341,15 +2336,23 @@ hash_rtx_cb (const_rtx x, enum machine_mode mode,
                + (unsigned int) INTVAL (x));
       return hash;
 
+    case CONST_WIDE_INT:
+      {
+       int i;
+       for (i = 0; i < CONST_WIDE_INT_NUNITS (x); i++)
+         hash += CONST_WIDE_INT_ELT (x, i);
+      }
+      return hash;
+
     case CONST_DOUBLE:
       /* This is like the general case, except that it only counts
         the integers representing the constant.  */
       hash += (unsigned int) code + (unsigned int) GET_MODE (x);
-      if (GET_MODE (x) != VOIDmode)
-       hash += real_hash (CONST_DOUBLE_REAL_VALUE (x));
-      else
+      if (TARGET_SUPPORTS_WIDE_INT == 0 && GET_MODE (x) == VOIDmode)
        hash += ((unsigned int) CONST_DOUBLE_LOW (x)
                 + (unsigned int) CONST_DOUBLE_HIGH (x));
+      else
+       hash += real_hash (CONST_DOUBLE_REAL_VALUE (x));
       return hash;
 
     case CONST_FIXED:
@@ -2555,7 +2558,7 @@ hash_rtx_cb (const_rtx x, enum machine_mode mode,
    Store 1 in DO_NOT_RECORD_P if any subexpression is volatile.
 
    If HASH_ARG_IN_MEMORY_P is not NULL, store 1 in it if X contains
-   a MEM rtx which does not have the RTX_UNCHANGING_P bit set.
+   a MEM rtx which does not have the MEM_READONLY_P flag set.
 
    Note that cse_insn knows that the hash code of a MEM expression
    is just (int) MEM plus the hash code of the address.  */
@@ -2571,7 +2574,7 @@ hash_rtx (const_rtx x, enum machine_mode mode, int *do_not_record_p,
 /* Hash an rtx X for cse via hash_rtx.
    Stores 1 in do_not_record if any subexpression is volatile.
    Stores 1 in hash_arg_in_memory if X contains a mem rtx which
-   does not have the RTX_UNCHANGING_P bit set.  */
+   does not have the MEM_READONLY_P flag set.  */
 
 static inline unsigned
 canon_hash (rtx x, enum machine_mode mode)
@@ -2621,7 +2624,7 @@ exp_equiv_p (const_rtx x, const_rtx y, int validate, bool for_gcse)
   if (GET_MODE (x) != GET_MODE (y))
     return 0;
 
-  /* MEMs refering to different address space are not equivalent.  */
+  /* MEMs referring to different address space are not equivalent.  */
   if (code == MEM && MEM_ADDR_SPACE (x) != MEM_ADDR_SPACE (y))
     return 0;
 
@@ -2629,9 +2632,7 @@ exp_equiv_p (const_rtx x, const_rtx y, int validate, bool for_gcse)
     {
     case PC:
     case CC0:
-    case CONST_INT:
-    case CONST_DOUBLE:
-    case CONST_FIXED:
+    CASE_CONST_UNIQUE:
       return x == y;
 
     case LABEL_REF:
@@ -2792,67 +2793,6 @@ exp_equiv_p (const_rtx x, const_rtx y, int validate, bool for_gcse)
   return 1;
 }
 \f
-/* Return 1 if X has a value that can vary even between two
-   executions of the program.  0 means X can be compared reliably
-   against certain constants or near-constants.  */
-
-static bool
-cse_rtx_varies_p (const_rtx x, bool from_alias)
-{
-  /* We need not check for X and the equivalence class being of the same
-     mode because if X is equivalent to a constant in some mode, it
-     doesn't vary in any mode.  */
-
-  if (REG_P (x)
-      && REGNO_QTY_VALID_P (REGNO (x)))
-    {
-      int x_q = REG_QTY (REGNO (x));
-      struct qty_table_elem *x_ent = &qty_table[x_q];
-
-      if (GET_MODE (x) == x_ent->mode
-         && x_ent->const_rtx != NULL_RTX)
-       return 0;
-    }
-
-  if (GET_CODE (x) == PLUS
-      && CONST_INT_P (XEXP (x, 1))
-      && REG_P (XEXP (x, 0))
-      && REGNO_QTY_VALID_P (REGNO (XEXP (x, 0))))
-    {
-      int x0_q = REG_QTY (REGNO (XEXP (x, 0)));
-      struct qty_table_elem *x0_ent = &qty_table[x0_q];
-
-      if ((GET_MODE (XEXP (x, 0)) == x0_ent->mode)
-         && x0_ent->const_rtx != NULL_RTX)
-       return 0;
-    }
-
-  /* This can happen as the result of virtual register instantiation, if
-     the initial constant is too large to be a valid address.  This gives
-     us a three instruction sequence, load large offset into a register,
-     load fp minus a constant into a register, then a MEM which is the
-     sum of the two `constant' registers.  */
-  if (GET_CODE (x) == PLUS
-      && REG_P (XEXP (x, 0))
-      && REG_P (XEXP (x, 1))
-      && REGNO_QTY_VALID_P (REGNO (XEXP (x, 0)))
-      && REGNO_QTY_VALID_P (REGNO (XEXP (x, 1))))
-    {
-      int x0_q = REG_QTY (REGNO (XEXP (x, 0)));
-      int x1_q = REG_QTY (REGNO (XEXP (x, 1)));
-      struct qty_table_elem *x0_ent = &qty_table[x0_q];
-      struct qty_table_elem *x1_ent = &qty_table[x1_q];
-
-      if ((GET_MODE (XEXP (x, 0)) == x0_ent->mode)
-         && x0_ent->const_rtx != NULL_RTX
-         && (GET_MODE (XEXP (x, 1)) == x1_ent->mode)
-         && x1_ent->const_rtx != NULL_RTX)
-       return 0;
-    }
-
-  return rtx_varies_p (x, from_alias);
-}
-\f
 /* Subroutine of canon_reg.  Pass *XLOC through canon_reg, and validate
    the result if necessary.  INSN is as for canon_reg.  */
 
@@ -2896,10 +2836,7 @@ canon_reg (rtx x, rtx insn)
     case PC:
     case CC0:
     case CONST:
-    case CONST_INT:
-    case CONST_DOUBLE:
-    case CONST_FIXED:
-    case CONST_VECTOR:
+    CASE_CONST_ANY:
     case SYMBOL_REF:
     case LABEL_REF:
     case ADDR_VEC:
@@ -2965,6 +2902,9 @@ find_comparison_args (enum rtx_code code, rtx *parg1, rtx *parg2,
                      enum machine_mode *pmode1, enum machine_mode *pmode2)
 {
   rtx arg1, arg2;
+  struct pointer_set_t *visited = NULL;
+  /* Set nonzero when we find something of interest.  */
+  rtx x = NULL;
 
   arg1 = *parg1, arg2 = *parg2;
 
@@ -2972,11 +2912,18 @@ find_comparison_args (enum rtx_code code, rtx *parg1, rtx *parg2,
 
   while (arg2 == CONST0_RTX (GET_MODE (arg1)))
     {
-      /* Set nonzero when we find something of interest.  */
-      rtx x = 0;
       int reverse_code = 0;
       struct table_elt *p = 0;
 
+      /* Remember state from previous iteration.  */
+      if (x)
+       {
+         if (!visited)
+           visited = pointer_set_create ();
+         pointer_set_insert (visited, x);
+         x = 0;
+       }
+
       /* If arg1 is a COMPARE, extract the comparison arguments from it.
         On machines with CC0, this is the only case that can occur, since
         fold_rtx will return the COMPARE or item being compared with zero
@@ -3053,6 +3000,10 @@ find_comparison_args (enum rtx_code code, rtx *parg1, rtx *parg2,
          if (! exp_equiv_p (p->exp, p->exp, 1, false))
            continue;
 
+         /* If it's a comparison we've used before, skip it.  */
+         if (visited && pointer_set_contains (visited, p->exp))
+           continue;
+
          if (GET_CODE (p->exp) == COMPARE
              /* Another possibility is that this machine has a compare insn
                 that includes the comparison code.  In that case, ARG1 would
@@ -3131,6 +3082,8 @@ find_comparison_args (enum rtx_code code, rtx *parg1, rtx *parg2,
   *pmode1 = GET_MODE (arg1), *pmode2 = GET_MODE (arg2);
   *parg1 = fold_rtx (arg1, 0), *parg2 = fold_rtx (arg2, 0);
 
+  if (visited)
+    pointer_set_destroy (visited);
   return code;
 }
 \f
@@ -3184,10 +3137,7 @@ fold_rtx (rtx x, rtx insn)
       return x;
 
     case CONST:
-    case CONST_INT:
-    case CONST_DOUBLE:
-    case CONST_FIXED:
-    case CONST_VECTOR:
+    CASE_CONST_ANY:
     case SYMBOL_REF:
     case LABEL_REF:
     case REG:
@@ -3249,12 +3199,9 @@ fold_rtx (rtx x, rtx insn)
            break;
 
          case CONST:
-         case CONST_INT:
+         CASE_CONST_ANY:
          case SYMBOL_REF:
          case LABEL_REF:
-         case CONST_DOUBLE:
-         case CONST_FIXED:
-         case CONST_VECTOR:
            const_arg = folded_arg;
            break;
 
@@ -3294,7 +3241,7 @@ fold_rtx (rtx x, rtx insn)
           argument.  */
        if (const_arg != 0
            && const_arg != folded_arg
-           && COST_IN (const_arg, code) <= COST_IN (folded_arg, code)
+           && COST_IN (const_arg, code, i) <= COST_IN (folded_arg, code, i)
 
            /* It's not safe to substitute the operand of a conversion
               operator with a constant, as the conversion's identity
@@ -3349,8 +3296,8 @@ fold_rtx (rtx x, rtx insn)
          break;
 
        new_rtx = simplify_unary_operation (code, mode,
-                                       const_arg0 ? const_arg0 : folded_arg0,
-                                       mode_arg0);
+                                           const_arg0 ? const_arg0 : folded_arg0,
+                                           mode_arg0);
       }
       break;
 
@@ -3525,9 +3472,10 @@ fold_rtx (rtx x, rtx insn)
        }
 
       {
-       rtx op0 = const_arg0 ? const_arg0 : folded_arg0;
-       rtx op1 = const_arg1 ? const_arg1 : folded_arg1;
-        new_rtx = simplify_relational_operation (code, mode, mode_arg0, op0, op1);
+       rtx op0 = const_arg0 ? const_arg0 : copy_rtx (folded_arg0);
+       rtx op1 = const_arg1 ? const_arg1 : copy_rtx (folded_arg1);
+       new_rtx = simplify_relational_operation (code, mode, mode_arg0,
+                                                op0, op1);
       }
       break;
 
@@ -3621,7 +3569,7 @@ fold_rtx (rtx x, rtx insn)
            {
              rtx y = lookup_as_function (XEXP (x, 0), PLUS);
              if (y && CONST_INT_P (XEXP (y, 1)))
-               return fold_rtx (plus_constant (copy_rtx (y),
+               return fold_rtx (plus_constant (mode, copy_rtx (y),
                                                -INTVAL (const_arg1)),
                                 NULL_RTX);
            }
@@ -3821,6 +3769,7 @@ equiv_constant (rtx x)
 
       /* See if we previously assigned a constant value to this SUBREG.  */
       if ((new_rtx = lookup_as_function (x, CONST_INT)) != 0
+         || (new_rtx = lookup_as_function (x, CONST_WIDE_INT)) != 0
           || (new_rtx = lookup_as_function (x, CONST_DOUBLE)) != 0
           || (new_rtx = lookup_as_function (x, CONST_FIXED)) != 0)
         return new_rtx;
@@ -3840,8 +3789,12 @@ equiv_constant (rtx x)
            }
        }
 
-      /* Otherwise see if we already have a constant for the inner REG.  */
+      /* Otherwise see if we already have a constant for the inner REG,
+        and if that is enough to calculate an equivalent constant for
+        the subreg.  Note that the upper bits of paradoxical subregs
+        are undefined, so they cannot be said to equal anything.  */
       if (REG_P (SUBREG_REG (x))
+         && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (imode)
          && (new_rtx = equiv_constant (SUBREG_REG (x))) != 0)
         return simplify_subreg (mode, new_rtx, imode, SUBREG_BYTE (x));
 
@@ -4144,10 +4097,22 @@ record_jump_cond (enum rtx_code code, enum machine_mode mode, rtx op0,
 }
 \f
 /* CSE processing for one instruction.
-   First simplify sources and addresses of all assignments
-   in the instruction, using previously-computed equivalents values.
-   Then install the new sources and destinations in the table
-   of available values.  */
+
+   Most "true" common subexpressions are mostly optimized away in GIMPLE,
+   but the few that "leak through" are cleaned up by cse_insn, and complex
+   addressing modes are often formed here.
+
+   The main function is cse_insn, and between here and that function
+   a couple of helper functions is defined to keep the size of cse_insn
+   within reasonable proportions.
+   
+   Data is shared between the main and helper functions via STRUCT SET,
+   that contains all data related for every set in the instruction that
+   is being processed.
+   
+   Note that cse_main processes all sets in the instruction.  Most
+   passes in GCC only process simple SET insns or single_set insns, but
+   CSE processes insns with multiple sets as well.  */
 
 /* Data on one SET contained in the instruction.  */
 
@@ -4183,50 +4148,93 @@ struct set
   /* Table entry for the destination address.  */
   struct table_elt *dest_addr_elt;
 };
+\f
+/* Special handling for (set REG0 REG1) where REG0 is the
+   "cheapest", cheaper than REG1.  After cse, REG1 will probably not
+   be used in the sequel, so (if easily done) change this insn to
+   (set REG1 REG0) and replace REG1 with REG0 in the previous insn
+   that computed their value.  Then REG1 will become a dead store
+   and won't cloud the situation for later optimizations.
+
+   Do not make this change if REG1 is a hard register, because it will
+   then be used in the sequel and we may be changing a two-operand insn
+   into a three-operand insn.
+   
+   This is the last transformation that cse_insn will try to do.  */
 
 static void
-cse_insn (rtx insn)
+try_back_substitute_reg (rtx set, rtx insn)
 {
-  rtx x = PATTERN (insn);
-  int i;
-  rtx tem;
-  int n_sets = 0;
+  rtx dest = SET_DEST (set);
+  rtx src = SET_SRC (set);
 
-  rtx src_eqv = 0;
-  struct table_elt *src_eqv_elt = 0;
-  int src_eqv_volatile = 0;
-  int src_eqv_in_memory = 0;
-  unsigned src_eqv_hash = 0;
+  if (REG_P (dest)
+      && REG_P (src) && ! HARD_REGISTER_P (src)
+      && REGNO_QTY_VALID_P (REGNO (src)))
+    {
+      int src_q = REG_QTY (REGNO (src));
+      struct qty_table_elem *src_ent = &qty_table[src_q];
 
-  struct set *sets = (struct set *) 0;
+      if (src_ent->first_reg == REGNO (dest))
+       {
+         /* Scan for the previous nonnote insn, but stop at a basic
+            block boundary.  */
+         rtx prev = insn;
+         rtx bb_head = BB_HEAD (BLOCK_FOR_INSN (insn));
+         do
+           {
+             prev = PREV_INSN (prev);
+           }
+         while (prev != bb_head && (NOTE_P (prev) || DEBUG_INSN_P (prev)));
 
-  this_insn = insn;
-#ifdef HAVE_cc0
-  /* Records what this insn does to set CC0.  */
-  this_insn_cc0 = 0;
-  this_insn_cc0_mode = VOIDmode;
-#endif
+         /* Do not swap the registers around if the previous instruction
+            attaches a REG_EQUIV note to REG1.
 
-  /* Find all the SETs and CLOBBERs in this instruction.
-     Record all the SETs in the array `set' and count them.
-     Also determine whether there is a CLOBBER that invalidates
-     all memory references, or all references at varying addresses.  */
+            ??? It's not entirely clear whether we can transfer a REG_EQUIV
+            from the pseudo that originally shadowed an incoming argument
+            to another register.  Some uses of REG_EQUIV might rely on it
+            being attached to REG1 rather than REG2.
 
-  if (CALL_P (insn))
-    {
-      for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
-       {
-         if (GET_CODE (XEXP (tem, 0)) == CLOBBER)
-           invalidate (SET_DEST (XEXP (tem, 0)), VOIDmode);
-         XEXP (tem, 0) = canon_reg (XEXP (tem, 0), insn);
+            This section previously turned the REG_EQUIV into a REG_EQUAL
+            note.  We cannot do that because REG_EQUIV may provide an
+            uninitialized stack slot when REG_PARM_STACK_SPACE is used.  */
+         if (NONJUMP_INSN_P (prev)
+             && GET_CODE (PATTERN (prev)) == SET
+             && SET_DEST (PATTERN (prev)) == src
+             && ! find_reg_note (prev, REG_EQUIV, NULL_RTX))
+           {
+             rtx note;
+
+             validate_change (prev, &SET_DEST (PATTERN (prev)), dest, 1);
+             validate_change (insn, &SET_DEST (set), src, 1);
+             validate_change (insn, &SET_SRC (set), dest, 1);
+             apply_change_group ();
+
+             /* If INSN has a REG_EQUAL note, and this note mentions
+                REG0, then we must delete it, because the value in
+                REG0 has changed.  If the note's value is REG1, we must
+                also delete it because that is now this insn's dest.  */
+             note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
+             if (note != 0
+                 && (reg_mentioned_p (dest, XEXP (note, 0))
+                     || rtx_equal_p (src, XEXP (note, 0))))
+               remove_note (insn, note);
+           }
        }
     }
+}
+\f
+/* Record all the SETs in this instruction into SETS_PTR,
+   and return the number of recorded sets.  */
+static int
+find_sets_in_insn (rtx insn, struct set **psets)
+{
+  struct set *sets = *psets;
+  int n_sets = 0;
+  rtx x = PATTERN (insn);
 
   if (GET_CODE (x) == SET)
     {
-      sets = XALLOCA (struct set);
-      sets[0].rtl = x;
-
       /* Ignore SETs that are unconditional jumps.
         They never need cse processing, so this does not hurt.
         The reason is not efficiency but rather
@@ -4237,57 +4245,20 @@ cse_insn (rtx insn)
       if (SET_DEST (x) == pc_rtx
          && GET_CODE (SET_SRC (x)) == LABEL_REF)
        ;
-
       /* Don't count call-insns, (set (reg 0) (call ...)), as a set.
         The hard function value register is used only once, to copy to
-        someplace else, so it isn't worth cse'ing (and on 80386 is unsafe)!
-        Ensure we invalidate the destination register.  On the 80386 no
-        other code would invalidate it since it is a fixed_reg.
-        We need not check the return of apply_change_group; see canon_reg.  */
-
+        someplace else, so it isn't worth cse'ing.  */
       else if (GET_CODE (SET_SRC (x)) == CALL)
-       {
-         canon_reg (SET_SRC (x), insn);
-         apply_change_group ();
-         fold_rtx (SET_SRC (x), insn);
-         invalidate (SET_DEST (x), VOIDmode);
-       }
+       ;
       else
-       n_sets = 1;
+       sets[n_sets++].rtl = x;
     }
   else if (GET_CODE (x) == PARALLEL)
     {
-      int lim = XVECLEN (x, 0);
-
-      sets = XALLOCAVEC (struct set, lim);
-
-      /* Find all regs explicitly clobbered in this insn,
-        and ensure they are not replaced with any other regs
-        elsewhere in this insn.
-        When a reg that is clobbered is also used for input,
-        we should presume that that is for a reason,
-        and we should not substitute some other register
-        which is not supposed to be clobbered.
-        Therefore, this loop cannot be merged into the one below
-        because a CALL may precede a CLOBBER and refer to the
-        value clobbered.  We must not let a canonicalization do
-        anything in that case.  */
-      for (i = 0; i < lim; i++)
-       {
-         rtx y = XVECEXP (x, 0, i);
-         if (GET_CODE (y) == CLOBBER)
-           {
-             rtx clobbered = XEXP (y, 0);
-
-             if (REG_P (clobbered)
-                 || GET_CODE (clobbered) == SUBREG)
-               invalidate (clobbered, VOIDmode);
-             else if (GET_CODE (clobbered) == STRICT_LOW_PART
-                      || GET_CODE (clobbered) == ZERO_EXTRACT)
-               invalidate (XEXP (clobbered, 0), GET_MODE (clobbered));
-           }
-       }
+      int i, lim = XVECLEN (x, 0);
 
+      /* Go over the epressions of the PARALLEL in forward order, to
+        put them in the same order in the SETS array.  */
       for (i = 0; i < lim; i++)
        {
          rtx y = XVECEXP (x, 0, i);
@@ -4295,50 +4266,81 @@ cse_insn (rtx insn)
            {
              /* As above, we ignore unconditional jumps and call-insns and
                 ignore the result of apply_change_group.  */
-             if (GET_CODE (SET_SRC (y)) == CALL)
-               {
-                 canon_reg (SET_SRC (y), insn);
-                 apply_change_group ();
-                 fold_rtx (SET_SRC (y), insn);
-                 invalidate (SET_DEST (y), VOIDmode);
-               }
-             else if (SET_DEST (y) == pc_rtx
-                      && GET_CODE (SET_SRC (y)) == LABEL_REF)
+             if (SET_DEST (y) == pc_rtx
+                 && GET_CODE (SET_SRC (y)) == LABEL_REF)
+               ;
+             else if (GET_CODE (SET_SRC (y)) == CALL)
                ;
              else
                sets[n_sets++].rtl = y;
            }
-         else if (GET_CODE (y) == CLOBBER)
-           {
-             /* If we clobber memory, canon the address.
-                This does nothing when a register is clobbered
-                because we have already invalidated the reg.  */
-             if (MEM_P (XEXP (y, 0)))
-               canon_reg (XEXP (y, 0), insn);
-           }
-         else if (GET_CODE (y) == USE
-                  && ! (REG_P (XEXP (y, 0))
-                        && REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER))
-           canon_reg (y, insn);
-         else if (GET_CODE (y) == CALL)
-           {
-             /* The result of apply_change_group can be ignored; see
-                canon_reg.  */
-             canon_reg (y, insn);
-             apply_change_group ();
-             fold_rtx (y, insn);
-           }
        }
     }
+
+  return n_sets;
+}
+\f
+/* Where possible, substitute every register reference in the N_SETS
+   number of SETS in INSN with the the canonical register.
+
+   Register canonicalization propagatest the earliest register (i.e.
+   one that is set before INSN) with the same value.  This is a very
+   useful, simple form of CSE, to clean up warts from expanding GIMPLE
+   to RTL.  For instance, a CONST for an address is usually expanded
+   multiple times to loads into different registers, thus creating many
+   subexpressions of the form:
+
+   (set (reg1) (some_const))
+   (set (mem (... reg1 ...) (thing)))
+   (set (reg2) (some_const))
+   (set (mem (... reg2 ...) (thing)))
+
+   After canonicalizing, the code takes the following form:
+
+   (set (reg1) (some_const))
+   (set (mem (... reg1 ...) (thing)))
+   (set (reg2) (some_const))
+   (set (mem (... reg1 ...) (thing)))
+
+   The set to reg2 is now trivially dead, and the memory reference (or
+   address, or whatever) may be a candidate for further CSEing.
+
+   In this function, the result of apply_change_group can be ignored;
+   see canon_reg.  */
+
+static void
+canonicalize_insn (rtx insn, struct set **psets, int n_sets)
+{
+  struct set *sets = *psets;
+  rtx tem;
+  rtx x = PATTERN (insn);
+  int i;
+
+  if (CALL_P (insn))
+    {
+      for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
+       if (GET_CODE (XEXP (tem, 0)) != SET)
+         XEXP (tem, 0) = canon_reg (XEXP (tem, 0), insn);
+    }
+
+  if (GET_CODE (x) == SET && GET_CODE (SET_SRC (x)) == CALL)
+    {
+      canon_reg (SET_SRC (x), insn);
+      apply_change_group ();
+      fold_rtx (SET_SRC (x), insn);
+    }
   else if (GET_CODE (x) == CLOBBER)
     {
+      /* If we clobber memory, canon the address.
+        This does nothing when a register is clobbered
+        because we have already invalidated the reg.  */
       if (MEM_P (XEXP (x, 0)))
        canon_reg (XEXP (x, 0), insn);
     }
-  /* Canonicalize a USE of a pseudo register or memory location.  */
   else if (GET_CODE (x) == USE
           && ! (REG_P (XEXP (x, 0))
                 && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER))
+    /* Canonicalize a USE of a pseudo register or memory location.  */
     canon_reg (x, insn);
   else if (GET_CODE (x) == ASM_OPERANDS)
     {
@@ -4354,29 +4356,60 @@ cse_insn (rtx insn)
     }
   else if (GET_CODE (x) == CALL)
     {
-      /* The result of apply_change_group can be ignored; see canon_reg.  */
       canon_reg (x, insn);
       apply_change_group ();
       fold_rtx (x, insn);
     }
   else if (DEBUG_INSN_P (insn))
     canon_reg (PATTERN (insn), insn);
+  else if (GET_CODE (x) == PARALLEL)
+    {
+      for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+       {
+         rtx y = XVECEXP (x, 0, i);
+         if (GET_CODE (y) == SET && GET_CODE (SET_SRC (y)) == CALL)
+           {
+             canon_reg (SET_SRC (y), insn);
+             apply_change_group ();
+             fold_rtx (SET_SRC (y), insn);
+           }
+         else if (GET_CODE (y) == CLOBBER)
+           {
+             if (MEM_P (XEXP (y, 0)))
+               canon_reg (XEXP (y, 0), insn);
+           }
+         else if (GET_CODE (y) == USE
+                  && ! (REG_P (XEXP (y, 0))
+                        && REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER))
+           canon_reg (y, insn);
+         else if (GET_CODE (y) == CALL)
+           {
+             canon_reg (y, insn);
+             apply_change_group ();
+             fold_rtx (y, insn);
+           }
+       }
+    }
 
-  /* Store the equivalent value in SRC_EQV, if different, or if the DEST
-     is a STRICT_LOW_PART.  The latter condition is necessary because SRC_EQV
-     is handled specially for this case, and if it isn't set, then there will
-     be no equivalence for the destination.  */
   if (n_sets == 1 && REG_NOTES (insn) != 0
-      && (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0
-      && (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))
-         || GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART))
+      && (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0)
     {
-      /* The result of apply_change_group can be ignored; see canon_reg.  */
-      canon_reg (XEXP (tem, 0), insn);
-      apply_change_group ();
-      src_eqv = fold_rtx (XEXP (tem, 0), insn);
-      XEXP (tem, 0) = copy_rtx (src_eqv);
-      df_notes_rescan (insn);
+      /* We potentially will process this insn many times.  Therefore,
+        drop the REG_EQUAL note if it is equal to the SET_SRC of the
+        unique set in INSN.
+
+        Do not do so if the REG_EQUAL note is for a STRICT_LOW_PART,
+        because cse_insn handles those specially.  */
+      if (GET_CODE (SET_DEST (sets[0].rtl)) != STRICT_LOW_PART
+         && rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl)))
+       remove_note (insn, tem);
+      else
+       {
+         canon_reg (XEXP (tem, 0), insn);
+         apply_change_group ();
+         XEXP (tem, 0) = fold_rtx (XEXP (tem, 0), insn);
+         df_notes_rescan (insn);
+       }
     }
 
   /* Canonicalize sources and addresses of destinations.
@@ -4423,6 +4456,62 @@ cse_insn (rtx insn)
      The result of apply_change_group can be ignored; see canon_reg.  */
 
   apply_change_group ();
+}
+\f
+/* Main function of CSE.
+   First simplify sources and addresses of all assignments
+   in the instruction, using previously-computed equivalents values.
+   Then install the new sources and destinations in the table
+   of available values.  */
+
+static void
+cse_insn (rtx insn)
+{
+  rtx x = PATTERN (insn);
+  int i;
+  rtx tem;
+  int n_sets = 0;
+
+  rtx src_eqv = 0;
+  struct table_elt *src_eqv_elt = 0;
+  int src_eqv_volatile = 0;
+  int src_eqv_in_memory = 0;
+  unsigned src_eqv_hash = 0;
+
+  struct set *sets = (struct set *) 0;
+
+  if (GET_CODE (x) == SET)
+    sets = XALLOCA (struct set);
+  else if (GET_CODE (x) == PARALLEL)
+    sets = XALLOCAVEC (struct set, XVECLEN (x, 0));
+
+  this_insn = insn;
+#ifdef HAVE_cc0
+  /* Records what this insn does to set CC0.  */
+  this_insn_cc0 = 0;
+  this_insn_cc0_mode = VOIDmode;
+#endif
+
+  /* Find all regs explicitly clobbered in this insn,
+     to ensure they are not replaced with any other regs
+     elsewhere in this insn.  */
+  invalidate_from_sets_and_clobbers (insn);
+
+  /* Record all the SETs in this instruction.  */
+  n_sets = find_sets_in_insn (insn, &sets);
+
+  /* Substitute the canonical register where possible.  */
+  canonicalize_insn (insn, &sets, n_sets);
+
+  /* If this insn has a REG_EQUAL note, store the equivalent value in SRC_EQV,
+     if different, or if the DEST is a STRICT_LOW_PART.  The latter condition
+     is necessary because SRC_EQV is handled specially for this case, and if
+     it isn't set, then there will be no equivalence for the destination.  */
+  if (n_sets == 1 && REG_NOTES (insn) != 0
+      && (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0
+      && (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))
+         || GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART))
+    src_eqv = copy_rtx (XEXP (tem, 0));
 
   /* Set sets[i].src_elt to the class each source belongs to.
      Detect assignments from or to volatile things
@@ -4759,7 +4848,7 @@ cse_insn (rtx insn)
 
          /* Set what we are trying to extend and the operation it might
             have been extended with.  */
-         memset (memory_extend_rtx, 0, sizeof(*memory_extend_rtx));
+         memset (memory_extend_rtx, 0, sizeof (*memory_extend_rtx));
          PUT_CODE (memory_extend_rtx, LOAD_EXTEND_OP (mode));
          XEXP (memory_extend_rtx, 0) = src;
 
@@ -5236,33 +5325,33 @@ cse_insn (rtx insn)
        }
 
       /* If this is a single SET, we are setting a register, and we have an
-        equivalent constant, we want to add a REG_NOTE.   We don't want
-        to write a REG_EQUAL note for a constant pseudo since verifying that
-        that pseudo hasn't been eliminated is a pain.  Such a note also
-        won't help anything.
+        equivalent constant, we want to add a REG_EQUAL note if the constant
+        is different from the source.  We don't want to do it for a constant
+        pseudo since verifying that this pseudo hasn't been eliminated is a
+        pain; moreover such a note won't help anything.
 
         Avoid a REG_EQUAL note for (CONST (MINUS (LABEL_REF) (LABEL_REF)))
         which can be created for a reference to a compile time computable
         entry in a jump table.  */
-
-      if (n_sets == 1 && src_const && REG_P (dest)
+      if (n_sets == 1
+         && REG_P (dest)
+         && src_const
          && !REG_P (src_const)
-         && ! (GET_CODE (src_const) == CONST
-               && GET_CODE (XEXP (src_const, 0)) == MINUS
-               && GET_CODE (XEXP (XEXP (src_const, 0), 0)) == LABEL_REF
-               && GET_CODE (XEXP (XEXP (src_const, 0), 1)) == LABEL_REF))
+         && !(GET_CODE (src_const) == SUBREG
+              && REG_P (SUBREG_REG (src_const)))
+         && !(GET_CODE (src_const) == CONST
+              && GET_CODE (XEXP (src_const, 0)) == MINUS
+              && GET_CODE (XEXP (XEXP (src_const, 0), 0)) == LABEL_REF
+              && GET_CODE (XEXP (XEXP (src_const, 0), 1)) == LABEL_REF)
+         && !rtx_equal_p (src, src_const))
        {
-         /* We only want a REG_EQUAL note if src_const != src.  */
-         if (! rtx_equal_p (src, src_const))
-           {
-             /* Make sure that the rtx is not shared.  */
-             src_const = copy_rtx (src_const);
+         /* Make sure that the rtx is not shared.  */
+         src_const = copy_rtx (src_const);
 
-             /* Record the actual constant value in a REG_EQUAL note,
-                making a new one if one does not already exist.  */
-             set_unique_reg_note (insn, REG_EQUAL, src_const);
-             df_notes_rescan (insn);
-           }
+         /* Record the actual constant value in a REG_EQUAL note,
+            making a new one if one does not already exist.  */
+         set_unique_reg_note (insn, REG_EQUAL, src_const);
+         df_notes_rescan (insn);
        }
 
       /* Now deal with the destination.  */
@@ -5306,7 +5395,7 @@ cse_insn (rtx insn)
              && CONST_INT_P (width)
              && INTVAL (width) < HOST_BITS_PER_WIDE_INT
              && ! (INTVAL (src_const)
-                   & ((HOST_WIDE_INT) (-1) << INTVAL (width))))
+                   & (HOST_WIDE_INT_M1U << INTVAL (width))))
            /* Exception: if the value is constant,
               and it won't be truncated, record it.  */
            ;
@@ -5547,7 +5636,7 @@ cse_insn (rtx insn)
        }
     }
 
-  invalidate_from_clobbers (x);
+  invalidate_from_clobbers (insn);
 
   /* Some registers are invalidated by subroutine calls.  Memory is
      invalidated by non-constant calls.  */
@@ -5584,10 +5673,9 @@ cse_insn (rtx insn)
          invalidate (XEXP (dest, 0), GET_MODE (dest));
       }
 
-  /* A volatile ASM invalidates everything.  */
+  /* A volatile ASM or an UNSPEC_VOLATILE invalidates everything.  */
   if (NONJUMP_INSN_P (insn)
-      && GET_CODE (PATTERN (insn)) == ASM_OPERANDS
-      && MEM_VOLATILE_P (PATTERN (insn)))
+      && volatile_insn_p (PATTERN (insn)))
     flush_hash_table ();
 
   /* Don't cse over a call to setjmp; on some machines (eg VAX)
@@ -5843,64 +5931,8 @@ cse_insn (rtx insn)
 
      Also do not do this if we are operating on a copy of INSN.  */
 
-  if (n_sets == 1 && sets[0].rtl && REG_P (SET_DEST (sets[0].rtl))
-      && NEXT_INSN (PREV_INSN (insn)) == insn
-      && REG_P (SET_SRC (sets[0].rtl))
-      && REGNO (SET_SRC (sets[0].rtl)) >= FIRST_PSEUDO_REGISTER
-      && REGNO_QTY_VALID_P (REGNO (SET_SRC (sets[0].rtl))))
-    {
-      int src_q = REG_QTY (REGNO (SET_SRC (sets[0].rtl)));
-      struct qty_table_elem *src_ent = &qty_table[src_q];
-
-      if (src_ent->first_reg == REGNO (SET_DEST (sets[0].rtl)))
-       {
-         /* Scan for the previous nonnote insn, but stop at a basic
-            block boundary.  */
-         rtx prev = insn;
-         rtx bb_head = BB_HEAD (BLOCK_FOR_INSN (insn));
-         do
-           {
-             prev = PREV_INSN (prev);
-           }
-         while (prev != bb_head && (NOTE_P (prev) || DEBUG_INSN_P (prev)));
-
-         /* Do not swap the registers around if the previous instruction
-            attaches a REG_EQUIV note to REG1.
-
-            ??? It's not entirely clear whether we can transfer a REG_EQUIV
-            from the pseudo that originally shadowed an incoming argument
-            to another register.  Some uses of REG_EQUIV might rely on it
-            being attached to REG1 rather than REG2.
-
-            This section previously turned the REG_EQUIV into a REG_EQUAL
-            note.  We cannot do that because REG_EQUIV may provide an
-            uninitialized stack slot when REG_PARM_STACK_SPACE is used.  */
-         if (NONJUMP_INSN_P (prev)
-             && GET_CODE (PATTERN (prev)) == SET
-             && SET_DEST (PATTERN (prev)) == SET_SRC (sets[0].rtl)
-             && ! find_reg_note (prev, REG_EQUIV, NULL_RTX))
-           {
-             rtx dest = SET_DEST (sets[0].rtl);
-             rtx src = SET_SRC (sets[0].rtl);
-             rtx note;
-
-             validate_change (prev, &SET_DEST (PATTERN (prev)), dest, 1);
-             validate_change (insn, &SET_DEST (sets[0].rtl), src, 1);
-             validate_change (insn, &SET_SRC (sets[0].rtl), dest, 1);
-             apply_change_group ();
-
-             /* If INSN has a REG_EQUAL note, and this note mentions
-                REG0, then we must delete it, because the value in
-                REG0 has changed.  If the note's value is REG1, we must
-                also delete it because that is now this insn's dest.  */
-             note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
-             if (note != 0
-                 && (reg_mentioned_p (dest, XEXP (note, 0))
-                     || rtx_equal_p (src, XEXP (note, 0))))
-               remove_note (insn, note);
-           }
-       }
-    }
+  if (n_sets == 1 && sets[0].rtl)
+    try_back_substitute_reg (sets[0].rtl, insn);
 
 done:;
 }
@@ -5922,16 +5954,16 @@ invalidate_memory (void)
       }
 }
 
-/* Perform invalidation on the basis of everything about an insn
+/* Perform invalidation on the basis of everything about INSN,
    except for invalidating the actual places that are SET in it.
    This includes the places CLOBBERed, and anything that might
-   alias with something that is SET or CLOBBERed.
-
-   X is the pattern of the insn.  */
+   alias with something that is SET or CLOBBERed.  */
 
 static void
-invalidate_from_clobbers (rtx x)
+invalidate_from_clobbers (rtx insn)
 {
+  rtx x = PATTERN (insn);
+
   if (GET_CODE (x) == CLOBBER)
     {
       rtx ref = XEXP (x, 0);
@@ -5965,6 +5997,53 @@ invalidate_from_clobbers (rtx x)
     }
 }
 \f
+/* Perform invalidation on the basis of everything about INSN.
+   This includes the places CLOBBERed, and anything that might
+   alias with something that is SET or CLOBBERed.  */
+
+static void
+invalidate_from_sets_and_clobbers (rtx insn)
+{
+  rtx tem;
+  rtx x = PATTERN (insn);
+
+  if (CALL_P (insn))
+    {
+      for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
+       if (GET_CODE (XEXP (tem, 0)) == CLOBBER)
+         invalidate (SET_DEST (XEXP (tem, 0)), VOIDmode);
+    }
+
+  /* Ensure we invalidate the destination register of a CALL insn.
+     This is necessary for machines where this register is a fixed_reg,
+     because no other code would invalidate it.  */
+  if (GET_CODE (x) == SET && GET_CODE (SET_SRC (x)) == CALL)
+    invalidate (SET_DEST (x), VOIDmode);
+
+  else if (GET_CODE (x) == PARALLEL)
+    {
+      int i;
+
+      for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+       {
+         rtx y = XVECEXP (x, 0, i);
+         if (GET_CODE (y) == CLOBBER)
+           {
+             rtx clobbered = XEXP (y, 0);
+
+             if (REG_P (clobbered)
+                 || GET_CODE (clobbered) == SUBREG)
+               invalidate (clobbered, VOIDmode);
+             else if (GET_CODE (clobbered) == STRICT_LOW_PART
+                      || GET_CODE (clobbered) == ZERO_EXTRACT)
+               invalidate (XEXP (clobbered, 0), GET_MODE (clobbered));
+           }
+         else if (GET_CODE (y) == SET && GET_CODE (SET_SRC (y)) == CALL)
+           invalidate (SET_DEST (y), VOIDmode);
+       }
+    }
+}
+\f
 /* Process X, part of the REG_NOTES of an insn.  Look at any REG_EQUAL notes
    and replace any registers in them with either an equivalent constant
    or the canonical form of the register.  If we are inside an address,
@@ -5983,13 +6062,10 @@ cse_process_notes_1 (rtx x, rtx object, bool *changed)
 
   switch (code)
     {
-    case CONST_INT:
     case CONST:
     case SYMBOL_REF:
     case LABEL_REF:
-    case CONST_DOUBLE:
-    case CONST_FIXED:
-    case CONST_VECTOR:
+    CASE_CONST_ANY:
     case PC:
     case CC0:
     case LO_SUM:
@@ -6001,9 +6077,12 @@ cse_process_notes_1 (rtx x, rtx object, bool *changed)
       return x;
 
     case EXPR_LIST:
-    case INSN_LIST:
       if (REG_NOTE_KIND (x) == REG_EQUAL)
        XEXP (x, 0) = cse_process_notes (XEXP (x, 0), NULL_RTX, changed);
+      /* Fall through.  */
+
+    case INSN_LIST:
+    case INT_LIST:
       if (XEXP (x, 1))
        XEXP (x, 1) = cse_process_notes (XEXP (x, 1), NULL_RTX, changed);
       return x;
@@ -6086,7 +6165,7 @@ cse_find_path (basic_block first_bb, struct cse_basic_block_data *data,
   edge e;
   int path_size;
 
-  SET_BIT (cse_visited_basic_blocks, first_bb->index);
+  bitmap_set_bit (cse_visited_basic_blocks, first_bb->index);
 
   /* See if there is a previous path.  */
   path_size = data->path_size;
@@ -6130,7 +6209,7 @@ cse_find_path (basic_block first_bb, struct cse_basic_block_data *data,
              && e == BRANCH_EDGE (previous_bb_in_path))
            {
              bb = FALLTHRU_EDGE (previous_bb_in_path)->dest;
-             if (bb != EXIT_BLOCK_PTR
+             if (bb != EXIT_BLOCK_PTR_FOR_FN (cfun)
                  && single_pred_p (bb)
                  /* We used to assert here that we would only see blocks
                     that we have not visited yet.  But we may end up
@@ -6143,9 +6222,9 @@ cse_find_path (basic_block first_bb, struct cse_basic_block_data *data,
 
                     We still want to visit each basic block only once, so
                     halt the path here if we have already visited BB.  */
-                 && !TEST_BIT (cse_visited_basic_blocks, bb->index))
+                 && !bitmap_bit_p (cse_visited_basic_blocks, bb->index))
                {
-                 SET_BIT (cse_visited_basic_blocks, bb->index);
+                 bitmap_set_bit (cse_visited_basic_blocks, bb->index);
                  data->path[path_size++].bb = bb;
                  break;
                }
@@ -6184,14 +6263,14 @@ cse_find_path (basic_block first_bb, struct cse_basic_block_data *data,
 
          if (e
              && !((e->flags & EDGE_ABNORMAL_CALL) && cfun->has_nonlocal_label)
-             && e->dest != EXIT_BLOCK_PTR
+             && e->dest != EXIT_BLOCK_PTR_FOR_FN (cfun)
              && single_pred_p (e->dest)
              /* Avoid visiting basic blocks twice.  The large comment
                 above explains why this can happen.  */
-             && !TEST_BIT (cse_visited_basic_blocks, e->dest->index))
+             && !bitmap_bit_p (cse_visited_basic_blocks, e->dest->index))
            {
              basic_block bb2 = e->dest;
-             SET_BIT (cse_visited_basic_blocks, bb2->index);
+             bitmap_set_bit (cse_visited_basic_blocks, bb2->index);
              data->path[path_size++].bb = bb2;
              bb = bb2;
            }
@@ -6403,7 +6482,7 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data)
                  /* If we truncate the path, we must also reset the
                     visited bit on the remaining blocks in the path,
                     or we will never visit them at all.  */
-                 RESET_BIT (cse_visited_basic_blocks,
+                 bitmap_clear_bit (cse_visited_basic_blocks,
                             ebb_data->path[path_size].bb->index);
                  ebb_data->path[path_size].bb = NULL;
                }
@@ -6447,7 +6526,7 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data)
    Return 1 if the CFG should be cleaned up because it has been modified.
    Return 0 otherwise.  */
 
-int
+static int
 cse_main (rtx f ATTRIBUTE_UNUSED, int nregs)
 {
   struct cse_basic_block_data ebb_data;
@@ -6456,6 +6535,7 @@ cse_main (rtx f ATTRIBUTE_UNUSED, int nregs)
   int i, n_blocks;
 
   df_set_flags (DF_LR_RUN_DCE);
+  df_note_add_problem ();
   df_analyze ();
   df_set_flags (DF_DEFER_INSN_RESCAN);
 
@@ -6481,7 +6561,7 @@ cse_main (rtx f ATTRIBUTE_UNUSED, int nregs)
 
   /* Set up the table of already visited basic blocks.  */
   cse_visited_basic_blocks = sbitmap_alloc (last_basic_block);
-  sbitmap_zero (cse_visited_basic_blocks);
+  bitmap_clear (cse_visited_basic_blocks);
 
   /* Loop over basic blocks in reverse completion order (RPO),
      excluding the ENTRY and EXIT blocks.  */
@@ -6495,7 +6575,7 @@ cse_main (rtx f ATTRIBUTE_UNUSED, int nregs)
        {
          bb = BASIC_BLOCK (rc_order[i++]);
        }
-      while (TEST_BIT (cse_visited_basic_blocks, bb->index)
+      while (bitmap_bit_p (cse_visited_basic_blocks, bb->index)
             && i < n_blocks);
 
       /* Find all paths starting with BB, and process them.  */
@@ -6591,10 +6671,7 @@ count_reg_usage (rtx x, int *counts, rtx dest, int incr)
     case PC:
     case CC0:
     case CONST:
-    case CONST_INT:
-    case CONST_DOUBLE:
-    case CONST_FIXED:
-    case CONST_VECTOR:
+    CASE_CONST_ANY:
     case SYMBOL_REF:
     case LABEL_REF:
       return;
@@ -6621,10 +6698,11 @@ count_reg_usage (rtx x, int *counts, rtx dest, int incr)
     case CALL_INSN:
     case INSN:
     case JUMP_INSN:
-      /* We expect dest to be NULL_RTX here.  If the insn may trap,
+      /* We expect dest to be NULL_RTX here.  If the insn may throw,
         or if it cannot be deleted due to side-effects, mark this fact
         by setting DEST to pc_rtx.  */
-      if (insn_could_throw_p (x) || side_effects_p (PATTERN (x)))
+      if ((!cfun->can_delete_dead_exceptions && !insn_nothrow_p (x))
+         || side_effects_p (PATTERN (x)))
        dest = pc_rtx;
       if (code == CALL_INSN)
        count_reg_usage (CALL_INSN_FUNCTION_USAGE (x), counts, dest, incr);
@@ -6670,6 +6748,7 @@ count_reg_usage (rtx x, int *counts, rtx dest, int incr)
       return;
 
     case INSN_LIST:
+    case INT_LIST:
       gcc_unreachable ();
 
     default:
@@ -6729,7 +6808,7 @@ static bool
 insn_live_p (rtx insn, int *counts)
 {
   int i;
-  if (insn_could_throw_p (insn))
+  if (!cfun->can_delete_dead_exceptions && !insn_nothrow_p (insn))
     return true;
   else if (GET_CODE (PATTERN (insn)) == SET)
     return set_live_p (PATTERN (insn), insn, counts);
@@ -7096,7 +7175,7 @@ cse_cc_succs (basic_block bb, basic_block orig_bb, rtx cc_reg, rtx cc_src,
        continue;
 
       if (EDGE_COUNT (e->dest->preds) != 1
-         || e->dest == EXIT_BLOCK_PTR
+         || e->dest == EXIT_BLOCK_PTR_FOR_FN (cfun)
          /* Avoid endless recursion on unreachable blocks.  */
          || e->dest == orig_bb)
        continue;
@@ -7379,7 +7458,7 @@ rest_of_handle_cse (void)
     {
       timevar_push (TV_JUMP);
       rebuild_jump_labels (get_insns ());
-      cleanup_cfg (0);
+      cleanup_cfg (CLEANUP_CFG_CHANGED);
       timevar_pop (TV_JUMP);
     }
   else if (tem == 1 || optimize > 1)
@@ -7388,27 +7467,45 @@ rest_of_handle_cse (void)
   return 0;
 }
 
-struct rtl_opt_pass pass_cse =
+namespace {
+
+const pass_data pass_data_cse =
 {
- {
-  RTL_PASS,
-  "cse1",                               /* name */
-  gate_handle_cse,                      /* gate */
-  rest_of_handle_cse,                  /* execute */
-  NULL,                                 /* sub */
-  NULL,                                 /* next */
-  0,                                    /* static_pass_number */
-  TV_CSE,                               /* tv_id */
-  0,                                    /* properties_required */
-  0,                                    /* properties_provided */
-  0,                                    /* properties_destroyed */
-  0,                                    /* todo_flags_start */
-  TODO_df_finish | TODO_verify_rtl_sharing |
-  TODO_ggc_collect |
-  TODO_verify_flow,                     /* todo_flags_finish */
- }
+  RTL_PASS, /* type */
+  "cse1", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  true, /* has_gate */
+  true, /* has_execute */
+  TV_CSE, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  ( TODO_df_finish | TODO_verify_rtl_sharing
+    | TODO_verify_flow ), /* todo_flags_finish */
 };
 
+class pass_cse : public rtl_opt_pass
+{
+public:
+  pass_cse (gcc::context *ctxt)
+    : rtl_opt_pass (pass_data_cse, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate () { return gate_handle_cse (); }
+  unsigned int execute () { return rest_of_handle_cse (); }
+
+}; // class pass_cse
+
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_cse (gcc::context *ctxt)
+{
+  return new pass_cse (ctxt);
+}
+
 
 static bool
 gate_handle_cse2 (void)
@@ -7439,7 +7536,7 @@ rest_of_handle_cse2 (void)
     {
       timevar_push (TV_JUMP);
       rebuild_jump_labels (get_insns ());
-      cleanup_cfg (0);
+      cleanup_cfg (CLEANUP_CFG_CHANGED);
       timevar_pop (TV_JUMP);
     }
   else if (tem == 1)
@@ -7450,27 +7547,45 @@ rest_of_handle_cse2 (void)
 }
 
 
-struct rtl_opt_pass pass_cse2 =
+namespace {
+
+const pass_data pass_data_cse2 =
 {
- {
-  RTL_PASS,
-  "cse2",                               /* name */
-  gate_handle_cse2,                     /* gate */
-  rest_of_handle_cse2,                 /* execute */
-  NULL,                                 /* sub */
-  NULL,                                 /* next */
-  0,                                    /* static_pass_number */
-  TV_CSE2,                              /* tv_id */
-  0,                                    /* properties_required */
-  0,                                    /* properties_provided */
-  0,                                    /* properties_destroyed */
-  0,                                    /* todo_flags_start */
-  TODO_df_finish | TODO_verify_rtl_sharing |
-  TODO_ggc_collect |
-  TODO_verify_flow                      /* todo_flags_finish */
- }
+  RTL_PASS, /* type */
+  "cse2", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  true, /* has_gate */
+  true, /* has_execute */
+  TV_CSE2, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  ( TODO_df_finish | TODO_verify_rtl_sharing
+    | TODO_verify_flow ), /* todo_flags_finish */
 };
 
+class pass_cse2 : public rtl_opt_pass
+{
+public:
+  pass_cse2 (gcc::context *ctxt)
+    : rtl_opt_pass (pass_data_cse2, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate () { return gate_handle_cse2 (); }
+  unsigned int execute () { return rest_of_handle_cse2 (); }
+
+}; // class pass_cse2
+
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_cse2 (gcc::context *ctxt)
+{
+  return new pass_cse2 (ctxt);
+}
+
 static bool
 gate_handle_cse_after_global_opts (void)
 {
@@ -7500,7 +7615,7 @@ rest_of_handle_cse_after_global_opts (void)
     {
       timevar_push (TV_JUMP);
       rebuild_jump_labels (get_insns ());
-      cleanup_cfg (0);
+      cleanup_cfg (CLEANUP_CFG_CHANGED);
       timevar_pop (TV_JUMP);
     }
   else if (tem == 1)
@@ -7510,23 +7625,43 @@ rest_of_handle_cse_after_global_opts (void)
   return 0;
 }
 
-struct rtl_opt_pass pass_cse_after_global_opts =
+namespace {
+
+const pass_data pass_data_cse_after_global_opts =
 {
- {
-  RTL_PASS,
-  "cse_local",                          /* name */
-  gate_handle_cse_after_global_opts,    /* gate */
-  rest_of_handle_cse_after_global_opts, /* execute */
-  NULL,                                 /* sub */
-  NULL,                                 /* next */
-  0,                                    /* static_pass_number */
-  TV_CSE,                               /* tv_id */
-  0,                                    /* properties_required */
-  0,                                    /* properties_provided */
-  0,                                    /* properties_destroyed */
-  0,                                    /* todo_flags_start */
-  TODO_df_finish | TODO_verify_rtl_sharing |
-  TODO_ggc_collect |
-  TODO_verify_flow                      /* todo_flags_finish */
- }
+  RTL_PASS, /* type */
+  "cse_local", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  true, /* has_gate */
+  true, /* has_execute */
+  TV_CSE, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  ( TODO_df_finish | TODO_verify_rtl_sharing
+    | TODO_verify_flow ), /* todo_flags_finish */
 };
+
+class pass_cse_after_global_opts : public rtl_opt_pass
+{
+public:
+  pass_cse_after_global_opts (gcc::context *ctxt)
+    : rtl_opt_pass (pass_data_cse_after_global_opts, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate () { return gate_handle_cse_after_global_opts (); }
+  unsigned int execute () {
+    return rest_of_handle_cse_after_global_opts ();
+  }
+
+}; // class pass_cse_after_global_opts
+
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_cse_after_global_opts (gcc::context *ctxt)
+{
+  return new pass_cse_after_global_opts (ctxt);
+}