OSDN Git Service

(fold_rtx, case PLUS): When seeing if negative of constant is around,
[pf3gnuchains/gcc-fork.git] / gcc / cse.c
index 5c9c6b0..260da29 100644 (file)
--- a/gcc/cse.c
+++ b/gcc/cse.c
@@ -1,5 +1,5 @@
 /* Common subexpression elimination for GNU compiler.
-   Copyright (C) 1987, 88, 89, 92, 93, 1994 Free Software Foundation, Inc.
+   Copyright (C) 1987, 88, 89, 92-6, 1997 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -15,7 +15,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 
 #include "config.h"
@@ -342,6 +343,11 @@ static int max_uid;
 
 static int cse_jumps_altered;
 
+/* Nonzero if we put a LABEL_REF into the hash table.  Since we may have put
+   it into an INSN without a REG_LABEL, we have to rerun jump after CSE
+   to put in the note.  */
+static int recorded_label_ref;
+
 /* canon_hash stores 1 in do_not_record
    if it notices a reference to CC0, PC, or some other volatile
    subexpression.  */
@@ -472,12 +478,24 @@ struct table_elt
   ((REG_USERVAR_P (N) && REGNO (N) < FIRST_PSEUDO_REGISTER)    \
    || CHEAP_REGNO (REGNO (N)))
 
-#define COST(X)                                                \
-  (GET_CODE (X) == REG                                 \
-   ? (CHEAP_REG (X) ? 0                                        \
-      : REGNO (X) >= FIRST_PSEUDO_REGISTER ? 1         \
-      : 2)                                             \
-   : rtx_cost (X, SET) * 2)
+#define COST(X)                                                                \
+  (GET_CODE (X) == REG                                                 \
+   ? (CHEAP_REG (X) ? 0                                                        \
+      : REGNO (X) >= FIRST_PSEUDO_REGISTER ? 1                         \
+      : 2)                                                             \
+   : ((GET_CODE (X) == SUBREG                                          \
+       && GET_CODE (SUBREG_REG (X)) == REG                             \
+       && GET_MODE_CLASS (GET_MODE (X)) == MODE_INT                    \
+       && GET_MODE_CLASS (GET_MODE (SUBREG_REG (X))) == MODE_INT       \
+       && (GET_MODE_SIZE (GET_MODE (X))                                        \
+          < GET_MODE_SIZE (GET_MODE (SUBREG_REG (X))))                 \
+       && subreg_lowpart_p (X)                                         \
+       && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (GET_MODE (X)),      \
+                                GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (X))))) \
+      ? (CHEAP_REG (SUBREG_REG (X)) ? 0                                        \
+        : REGNO (SUBREG_REG (X)) >= FIRST_PSEUDO_REGISTER ? 1          \
+        : 2)                                                           \
+      : rtx_cost (X, SET) * 2))
 
 /* Determine if the quantity number for register X represents a valid index
    into the `qty_...' variables.  */
@@ -528,7 +546,7 @@ static int constant_pool_entries_cost;
 
 struct write_data
 {
-  int sp : 1;                  /* Invalidate stack pointer. */
+  int sp : 1;                  /* Invalidate stack pointer.  */
   int var : 1;                 /* Invalidate variable addresses.  */
   int nonscalar : 1;           /* Invalidate all but scalar variables.  */
   int all : 1;                 /* Invalidate all memory refs.  */
@@ -553,7 +571,7 @@ struct cse_basic_block_data {
   int path_size;
   /* Current branch path, indicating which branches will be taken.  */
   struct branch_path {
-    /* The branch insn. */
+    /* The branch insn.  */
     rtx branch;
     /* Whether it should be taken or not.  AROUND is the same as taken
        except that it is used when the destination label is not preceded
@@ -618,7 +636,7 @@ static struct table_elt *insert PROTO((rtx, struct table_elt *, unsigned,
                                       enum machine_mode));
 static void merge_equiv_classes PROTO((struct table_elt *,
                                       struct table_elt *));
-static void invalidate         PROTO((rtx));
+static void invalidate         PROTO((rtx, enum machine_mode));
 static void remove_invalid_refs        PROTO((int));
 static void rehash_using_reg   PROTO((rtx));
 static void invalidate_memory  PROTO((struct write_data *));
@@ -1286,6 +1304,11 @@ insert (x, classp, hash, mode)
            SET_HARD_REG_BIT (hard_regs_in_table, i);
     }
 
+  /* If X is a label, show we recorded it.  */
+  if (GET_CODE (x) == LABEL_REF
+      || (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS
+         && GET_CODE (XEXP (XEXP (x, 0), 0)) == LABEL_REF))
+    recorded_label_ref = 1;
 
   /* Put an element for X into the right hash bucket.  */
 
@@ -1360,20 +1383,22 @@ insert (x, classp, hash, mode)
      update `qty_const_insn' to show that `this_insn' is the latest
      insn making that quantity equivalent to the constant.  */
 
-  if (elt->is_const && classp && GET_CODE (classp->exp) == REG)
+  if (elt->is_const && classp && GET_CODE (classp->exp) == REG
+      && GET_CODE (x) != REG)
     {
       qty_const[reg_qty[REGNO (classp->exp)]]
        = gen_lowpart_if_possible (qty_mode[reg_qty[REGNO (classp->exp)]], x);
       qty_const_insn[reg_qty[REGNO (classp->exp)]] = this_insn;
     }
 
-  else if (GET_CODE (x) == REG && classp && ! qty_const[reg_qty[REGNO (x)]])
+  else if (GET_CODE (x) == REG && classp && ! qty_const[reg_qty[REGNO (x)]]
+          && ! elt->is_const)
     {
       register struct table_elt *p;
 
       for (p = classp; p != 0; p = p->next_same_value)
        {
-         if (p->is_const)
+         if (p->is_const && GET_CODE (p->exp) != REG)
            {
              qty_const[reg_qty[REGNO (x)]]
                = gen_lowpart_if_possible (GET_MODE (x), p->exp);
@@ -1453,7 +1478,7 @@ merge_equiv_classes (class1, class2)
 
       /* Remove old entry, make a new one in CLASS1's class.
         Don't do this for invalid entries as we cannot find their
-        hash code (it also isn't necessary). */
+        hash code (it also isn't necessary).  */
       if (GET_CODE (exp) == REG || exp_equiv_p (exp, exp, 1, 0))
        {
          hash_arg_in_memory = 0;
@@ -1466,7 +1491,10 @@ merge_equiv_classes (class1, class2)
          remove_from_table (elt, hash);
 
          if (insert_regs (exp, class1, 0))
-           hash = HASH (exp, mode);
+           {
+             rehash_using_reg (exp);
+             hash = HASH (exp, mode);
+           }
          new = insert (exp, class1, hash, mode);
          new->in_memory = hash_arg_in_memory;
          new->in_struct = hash_arg_in_struct;
@@ -1480,14 +1508,18 @@ merge_equiv_classes (class1, class2)
    (because, when a memory reference with a varying address is stored in,
    all memory references are removed by invalidate_memory
    so specific invalidation is superfluous).
+   FULL_MODE, if not VOIDmode, indicates that this much should be invalidated
+   instead of just the amount indicated by the mode of X.  This is only used
+   for bitfield stores into memory.
 
    A nonvarying address may be just a register or just
    a symbol reference, or it may be either of those plus
    a numeric offset.  */
 
 static void
-invalidate (x)
+invalidate (x, full_mode)
      rtx x;
+     enum machine_mode full_mode;
 {
   register int i;
   register struct table_elt *p;
@@ -1518,7 +1550,15 @@ invalidate (x)
       reg_tick[regno]++;
 
       if (regno >= FIRST_PSEUDO_REGISTER)
-       remove_from_table (lookup_for_remove (x, hash, GET_MODE (x)), hash);
+       {
+         /* Because a register can be referenced in more than one mode,
+            we might have to remove more than one table entry.  */
+
+         struct table_elt *elt;
+
+         while (elt = lookup_for_remove (x, hash, GET_MODE (x)))
+           remove_from_table (elt, hash);
+       }
       else
        {
          HOST_WIDE_INT in_table
@@ -1562,7 +1602,7 @@ invalidate (x)
     {
       if (GET_CODE (SUBREG_REG (x)) != REG)
        abort ();
-      invalidate (SUBREG_REG (x));
+      invalidate (SUBREG_REG (x), VOIDmode);
       return;
     }
 
@@ -1573,7 +1613,10 @@ invalidate (x)
   if (GET_CODE (x) != MEM)
     abort ();
 
-  set_nonvarying_address_components (XEXP (x, 0), GET_MODE_SIZE (GET_MODE (x)),
+  if (full_mode == VOIDmode)
+    full_mode = GET_MODE (x);
+
+  set_nonvarying_address_components (XEXP (x, 0), GET_MODE_SIZE (full_mode),
                                     &base, &start, &end);
 
   for (i = 0; i < NBUCKETS; i++)
@@ -1855,12 +1898,13 @@ canon_hash (x, mode)
        /* On some machines, we can't record any non-fixed hard register,
           because extending its life will cause reload problems.  We
           consider ap, fp, and sp to be fixed for this purpose.
-          On all machines, we can't record any global registers. */
+          On all machines, we can't record any global registers.  */
 
        if (regno < FIRST_PSEUDO_REGISTER
            && (global_regs[regno]
 #ifdef SMALL_REGISTER_CLASSES
-               || (! fixed_regs[regno]
+               || (SMALL_REGISTER_CLASSES
+                   && ! fixed_regs[regno]
                    && regno != FRAME_POINTER_REGNUM
                    && regno != HARD_FRAME_POINTER_REGNUM
                    && regno != ARG_POINTER_REGNUM
@@ -1886,11 +1930,15 @@ canon_hash (x, mode)
       /* This is like the general case, except that it only counts
         the integers representing the constant.  */
       hash += (unsigned) code + (unsigned) GET_MODE (x);
-      for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++)
-       {
-         unsigned tem = XINT (x, i);
-         hash += tem;
-       }
+      if (GET_MODE (x) != VOIDmode)
+       for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++)
+         {
+           unsigned tem = XINT (x, i);
+           hash += tem;
+         }
+      else
+       hash += ((unsigned) CONST_DOUBLE_LOW (x)
+                + (unsigned) CONST_DOUBLE_HIGH (x));
       return hash;
 
       /* Assume there is only one rtx object for any given label.  */
@@ -1910,7 +1958,7 @@ canon_hash (x, mode)
          do_not_record = 1;
          return 0;
        }
-      if (! RTX_UNCHANGING_P (x))
+      if (! RTX_UNCHANGING_P (x) || FIXED_BASE_PLUS_P (XEXP (x, 0)))
        {
          hash_arg_in_memory = 1;
          if (MEM_IN_STRUCT_P (x)) hash_arg_in_struct = 1;
@@ -1948,17 +1996,6 @@ canon_hash (x, mode)
       if (fmt[i] == 'e')
        {
          rtx tem = XEXP (x, i);
-         rtx tem1;
-
-         /* If the operand is a REG that is equivalent to a constant, hash
-            as if we were hashing the constant, since we will be comparing
-            that way.  */
-         if (tem != 0 && GET_CODE (tem) == REG
-             && REGNO_QTY_VALID_P (REGNO (tem))
-             && qty_mode[reg_qty[REGNO (tem)]] == GET_MODE (tem)
-             && (tem1 = qty_const[reg_qty[REGNO (tem)]]) != 0
-             && CONSTANT_P (tem1))
-           tem = tem1;
 
          /* If we are about to do the last recursive call
             needed at this level, change it into iteration.
@@ -2230,9 +2267,10 @@ refers_to_p (x, y)
    set PBASE, PSTART, and PEND which correspond to the base of the address,
    the starting offset, and ending offset respectively.
 
-   ADDR is known to be a nonvarying address. 
+   ADDR is known to be a nonvarying address.  */
 
-   cse_address_varies_p returns zero for nonvarying addresses.  */
+/* ??? Despite what the comments say, this function is in fact frequently
+   passed varying addresses.  This does not appear to cause any problems.  */
 
 static void
 set_nonvarying_address_components (addr, size, pbase, pstart, pend)
@@ -2268,6 +2306,37 @@ set_nonvarying_address_components (addr, size, pbase, pstart, pend)
       start = INTVAL (XEXP (base, 1));
       base = qty_const[reg_qty[REGNO (XEXP (base, 0))]];
     }
+  /* This can happen as the result of virtual register instantiation,
+     if the initial offset is too large to be a valid address.  */
+  else if (GET_CODE (base) == PLUS
+          && GET_CODE (XEXP (base, 0)) == REG
+          && GET_CODE (XEXP (base, 1)) == REG
+          && qty_const != 0
+          && REGNO_QTY_VALID_P (REGNO (XEXP (base, 0)))
+          && (qty_mode[reg_qty[REGNO (XEXP (base, 0))]]
+              == GET_MODE (XEXP (base, 0)))
+          && qty_const[reg_qty[REGNO (XEXP (base, 0))]]
+          && REGNO_QTY_VALID_P (REGNO (XEXP (base, 1)))
+          && (qty_mode[reg_qty[REGNO (XEXP (base, 1))]]
+              == GET_MODE (XEXP (base, 1)))
+          && qty_const[reg_qty[REGNO (XEXP (base, 1))]])
+    {
+      rtx tem = qty_const[reg_qty[REGNO (XEXP (base, 1))]];
+      base = qty_const[reg_qty[REGNO (XEXP (base, 0))]];
+
+      /* One of the two values must be a constant.  */
+      if (GET_CODE (base) != CONST_INT)
+       {
+         if (GET_CODE (tem) != CONST_INT)
+           abort ();
+         start = INTVAL (tem);
+       }
+      else
+       {
+         start = INTVAL (base);
+         base = tem;
+       }
+    }
 
   /* Handle everything that we can find inside an address that has been
      viewed as constant.  */
@@ -2314,7 +2383,8 @@ set_nonvarying_address_components (addr, size, pbase, pstart, pend)
                 constant we already had.  */
 
              size = *pend - *pstart - INTVAL (XEXP (base, 1)) - 1;
-             start += *pstart - INTVAL (XEXP (base, 1)) - 1;
+             start += *pstart + INTVAL (XEXP (base, 1)) + 1;
+             end += *pend;
              base = *pbase;
            }
          break;
@@ -2438,6 +2508,25 @@ cse_rtx_addr_varies_p (x)
       && qty_const[reg_qty[REGNO (XEXP (XEXP (x, 0), 0))]])
     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) == MEM
+      && GET_CODE (XEXP (x, 0)) == PLUS
+      && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
+      && GET_CODE (XEXP (XEXP (x, 0), 1)) == REG
+      && REGNO_QTY_VALID_P (REGNO (XEXP (XEXP (x, 0), 0)))
+      && (GET_MODE (XEXP (XEXP (x, 0), 0))
+         == qty_mode[reg_qty[REGNO (XEXP (XEXP (x, 0), 0))]])
+      && qty_const[reg_qty[REGNO (XEXP (XEXP (x, 0), 0))]]
+      && REGNO_QTY_VALID_P (REGNO (XEXP (XEXP (x, 0), 1)))
+      && (GET_MODE (XEXP (XEXP (x, 0), 1))
+         == qty_mode[reg_qty[REGNO (XEXP (XEXP (x, 0), 1))]])
+      && qty_const[reg_qty[REGNO (XEXP (XEXP (x, 0), 1))]])
+    return 0;
+
   return rtx_addr_varies_p (x);
 }
 \f
@@ -2506,6 +2595,7 @@ canon_reg (x, insn)
       if (fmt[i] == 'e')
        {
          rtx new = canon_reg (XEXP (x, i), insn);
+         int insn_code;
 
          /* If replacing pseudo with hard reg or vice versa, ensure the
             insn remains valid.  Likewise if the insn has MATCH_DUPs.  */
@@ -2513,7 +2603,8 @@ canon_reg (x, insn)
              && GET_CODE (new) == REG && GET_CODE (XEXP (x, i)) == REG
              && (((REGNO (new) < FIRST_PSEUDO_REGISTER)
                   != (REGNO (XEXP (x, i)) < FIRST_PSEUDO_REGISTER))
-                 || insn_n_dups[recog_memoized (insn)] > 0))
+                 || (insn_code = recog_memoized (insn)) < 0
+                 || insn_n_dups[insn_code] > 0))
            validate_change (insn, &XEXP (x, i), new, 1);
          else
            XEXP (x, i) = new;
@@ -2526,7 +2617,7 @@ canon_reg (x, insn)
   return x;
 }
 \f
-/* LOC is a location with INSN that is an operand address (the contents of
+/* LOC is a location within INSN that is an operand address (the contents of
    a MEM).  Find the best equivalent address to use that is valid for this
    insn.
 
@@ -2584,9 +2675,21 @@ find_best_addr (insn, loc)
      sometimes simplify the expression.  Many simplifications
      will not be valid, but some, usually applying the associative rule, will
      be valid and produce better code.  */
-  if (GET_CODE (addr) != REG
-      && validate_change (insn, loc, fold_rtx (addr, insn), 0))
-    addr = *loc;
+  if (GET_CODE (addr) != REG)
+    {
+      rtx folded = fold_rtx (copy_rtx (addr), NULL_RTX);
+
+      if (1
+#ifdef ADDRESS_COST
+         && (ADDRESS_COST (folded) < ADDRESS_COST (addr)
+             || (ADDRESS_COST (folded) == ADDRESS_COST (addr)
+                 && rtx_cost (folded) > rtx_cost (addr)))
+#else
+         && rtx_cost (folded) < rtx_cost (addr)
+#endif
+         && validate_change (insn, loc, folded, 0))
+       addr = folded;
+    }
        
   /* If this address is not in the hash table, we can't look for equivalences
      of the whole address.  Also, ignore if volatile.  */
@@ -2940,7 +3043,7 @@ simplify_unary_operation (code, mode, op, op_mode)
        lv = CONST_DOUBLE_LOW (op),  hv = CONST_DOUBLE_HIGH (op);
 
 #ifdef REAL_ARITHMETIC
-      REAL_VALUE_FROM_INT (d, lv, hv);
+      REAL_VALUE_FROM_INT (d, lv, hv, mode);
 #else
       if (hv < 0)
        {
@@ -2958,7 +3061,7 @@ simplify_unary_operation (code, mode, op, op_mode)
          d += (double) (unsigned HOST_WIDE_INT) lv;
        }
 #endif  /* REAL_ARITHMETIC */
-
+      d = real_value_truncate (mode, d);
       return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
     }
   else if (code == UNSIGNED_FLOAT && GET_MODE (op) == VOIDmode
@@ -2972,13 +3075,20 @@ simplify_unary_operation (code, mode, op, op_mode)
       else
        lv = CONST_DOUBLE_LOW (op),  hv = CONST_DOUBLE_HIGH (op);
 
-      if (GET_MODE_BITSIZE (op_mode) >= HOST_BITS_PER_WIDE_INT * 2)
+      if (op_mode == VOIDmode)
+       {
+         /* We don't know how to interpret negative-looking numbers in
+            this case, so don't try to fold those.  */
+         if (hv < 0)
+           return 0;
+       }
+      else if (GET_MODE_BITSIZE (op_mode) >= HOST_BITS_PER_WIDE_INT * 2)
        ;
       else
        hv = 0, lv &= GET_MODE_MASK (op_mode);
 
 #ifdef REAL_ARITHMETIC
-      REAL_VALUE_FROM_UNSIGNED_INT (d, lv, hv);
+      REAL_VALUE_FROM_UNSIGNED_INT (d, lv, hv, mode);
 #else
 
       d = (double) (unsigned HOST_WIDE_INT) hv;
@@ -2986,7 +3096,7 @@ simplify_unary_operation (code, mode, op, op_mode)
            * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
       d += (double) (unsigned HOST_WIDE_INT) lv;
 #endif  /* REAL_ARITHMETIC */
-
+      d = real_value_truncate (mode, d);
       return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
     }
 #endif
@@ -3078,13 +3188,13 @@ simplify_unary_operation (code, mode, op, op_mode)
       if (width < HOST_BITS_PER_WIDE_INT
          && ((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
              != ((HOST_WIDE_INT) (-1) << (width - 1))))
-       val &= (1 << width) - 1;
+       val &= ((HOST_WIDE_INT) 1 << width) - 1;
 
       return GEN_INT (val);
     }
 
   /* We can do some operations on integer CONST_DOUBLEs.  Also allow
-     for a DImode operation on a CONST_INT. */
+     for a DImode operation on a CONST_INT.  */
   else if (GET_MODE (op) == VOIDmode && width <= HOST_BITS_PER_INT * 2
           && (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT))
     {
@@ -3123,6 +3233,7 @@ simplify_unary_operation (code, mode, op, op_mode)
 
        case TRUNCATE:
          /* This is just a change-of-mode, so do nothing.  */
+         lv = l1, hv = h1;
          break;
 
        case ZERO_EXTEND:
@@ -3258,6 +3369,19 @@ simplify_unary_operation (code, mode, op, op_mode)
              != ((HOST_WIDE_INT) (-1) << (width - 1))))
        val &= ((HOST_WIDE_INT) 1 << width) - 1;
 
+      /* If this would be an entire word for the target, but is not for
+        the host, then sign-extend on the host so that the number will look
+        the same way on the host that it would on the target.
+
+        For example, when building a 64 bit alpha hosted 32 bit sparc
+        targeted compiler, then we want the 32 bit unsigned value -1 to be
+        represented as a 64 bit value -1, and not as 0x00000000ffffffff.
+        The later confuses the sparc backend.  */
+
+      if (BITS_PER_WORD < HOST_BITS_PER_WIDE_INT && BITS_PER_WORD == width
+         && (val & ((HOST_WIDE_INT) 1 << (width - 1))))
+       val |= ((HOST_WIDE_INT) (-1) << width);
+
       return GEN_INT (val);
     }
 #endif
@@ -3287,7 +3411,23 @@ simplify_unary_operation (code, mode, op, op_mode)
              && GET_CODE (XEXP (XEXP (op, 0), 0)) == LABEL_REF
              && GET_CODE (XEXP (XEXP (op, 0), 1)) == LABEL_REF)
            return XEXP (op, 0);
+
+#ifdef POINTERS_EXTEND_UNSIGNED
+         if (! POINTERS_EXTEND_UNSIGNED
+             && mode == Pmode && GET_MODE (op) == ptr_mode
+             && CONSTANT_P (op))
+           return convert_memory_address (Pmode, op);
+#endif
          break;
+
+#ifdef POINTERS_EXTEND_UNSIGNED
+       case ZERO_EXTEND:
+         if (POINTERS_EXTEND_UNSIGNED
+             && mode == Pmode && GET_MODE (op) == ptr_mode
+             && CONSTANT_P (op))
+           return convert_memory_address (Pmode, op);
+         break;
+#endif
        }
 
       return 0;
@@ -3400,7 +3540,7 @@ simplify_binary_operation (code, mode, op0, op1)
          neg_double (l2, h2, &lv, &hv);
          l2 = lv, h2 = hv;
 
-         /* .. fall through ... */
+         /* .. fall through ...  */
 
        case PLUS:
          add_double (l1, h1, l2, h2, &lv, &hv);
@@ -3717,6 +3857,15 @@ simplify_binary_operation (code, mode, op0, op1)
          /* Don't let a relocatable value get a negative coeff.  */
          if (GET_CODE (op1) == CONST_INT && GET_MODE (op0) != VOIDmode)
            return plus_constant (op0, - INTVAL (op1));
+
+         /* (x - (x & y)) -> (x & ~y) */
+         if (GET_CODE (op1) == AND)
+           {
+            if (rtx_equal_p (op0, XEXP (op1, 0)))
+              return cse_gen_binary (AND, mode, op0, gen_rtx (NOT, mode, XEXP (op1, 1)));
+            if (rtx_equal_p (op0, XEXP (op1, 1)))
+              return cse_gen_binary (AND, mode, op0, gen_rtx (NOT, mode, XEXP (op1, 0)));
+          }
          break;
 
        case MULT:
@@ -3744,6 +3893,11 @@ simplify_binary_operation (code, mode, op0, op1)
             we are still generating RTL.  This test is a kludge.  */
          if (GET_CODE (op1) == CONST_INT
              && (val = exact_log2 (INTVAL (op1))) >= 0
+             /* If the mode is larger than the host word size, and the
+                uppermost bit is set, then this isn't a power of two due
+                to implicit sign extension.  */
+             && (width <= HOST_BITS_PER_WIDE_INT
+                 || val != HOST_BITS_PER_WIDE_INT - 1)
              && ! rtx_equal_function_value_matters)
            return gen_rtx (ASHIFT, mode, op0, GEN_INT (val));
 
@@ -3823,7 +3977,7 @@ simplify_binary_operation (code, mode, op0, op1)
              && (arg1 = exact_log2 (INTVAL (op1))) > 0)
            return gen_rtx (LSHIFTRT, mode, op0, GEN_INT (arg1));
 
-         /* ... fall through ... */
+         /* ... fall through ...  */
 
        case DIV:
          if (op1 == CONST1_RTX (mode))
@@ -3869,7 +4023,7 @@ simplify_binary_operation (code, mode, op0, op1)
              && exact_log2 (INTVAL (op1)) > 0)
            return gen_rtx (AND, mode, op0, GEN_INT (INTVAL (op1) - 1));
 
-         /* ... fall through ... */
+         /* ... fall through ...  */
 
        case MOD:
          if ((op0 == const0_rtx || op1 == const1_rtx)
@@ -3885,7 +4039,7 @@ simplify_binary_operation (code, mode, op0, op1)
              && ! side_effects_p (op1))
            return op0;
 
-         /* ... fall through ... */
+         /* ... fall through ...  */
 
        case ASHIFT:
        case ASHIFTRT:
@@ -4109,6 +4263,19 @@ simplify_binary_operation (code, mode, op0, op1)
          != ((HOST_WIDE_INT) (-1) << (width - 1))))
     val &= ((HOST_WIDE_INT) 1 << width) - 1;
 
+  /* If this would be an entire word for the target, but is not for
+     the host, then sign-extend on the host so that the number will look
+     the same way on the host that it would on the target.
+
+     For example, when building a 64 bit alpha hosted 32 bit sparc
+     targeted compiler, then we want the 32 bit unsigned value -1 to be
+     represented as a 64 bit value -1, and not as 0x00000000ffffffff.
+     The later confuses the sparc backend.  */
+
+  if (BITS_PER_WORD < HOST_BITS_PER_WIDE_INT && BITS_PER_WORD == width
+      && (val & ((HOST_WIDE_INT) 1 << (width - 1))))
+    val |= ((HOST_WIDE_INT) (-1) << width);
+
   return GEN_INT (val);
 }
 \f
@@ -4361,20 +4528,16 @@ simplify_relational_operation (code, mode, op0, op1)
      a register or a CONST_INT, this can't help; testing for these cases will
      prevent infinite recursion here and speed things up.
 
-     If CODE is an unsigned comparison, we can only do this if A - B is a
-     constant integer, and then we have to compare that integer with zero as a
-     signed comparison.  Note that this will give the incorrect result from
-     comparisons that overflow.  Since these are undefined, this is probably
-     OK.  If it causes a problem, we can check for A or B being an address
-     (fp + const or SYMBOL_REF) and only do it in that case.  */
+     If CODE is an unsigned comparison, then we can never do this optimization,
+     because it gives an incorrect result if the subtraction wraps around zero.
+     ANSI C defines unsigned operations such that they never overflow, and
+     thus such cases can not be ignored.  */
 
   if (INTEGRAL_MODE_P (mode) && op1 != const0_rtx
       && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == CONST_INT)
            && (GET_CODE (op1) == REG || GET_CODE (op1) == CONST_INT))
       && 0 != (tem = simplify_binary_operation (MINUS, mode, op0, op1))
-      && (GET_CODE (tem) == CONST_INT
-         || (code != GTU && code != GEU &&
-             code != LTU && code != LEU)))
+      && code != GTU && code != GEU && code != LTU && code != LEU)
     return simplify_relational_operation (signed_condition (code),
                                          mode, tem, const0_rtx);
 
@@ -4580,11 +4743,12 @@ simplify_ternary_operation (code, mode, op0_mode, op0, op1, op2)
          /* Extracting a bit-field from a constant */
          HOST_WIDE_INT val = INTVAL (op0);
 
-#if BITS_BIG_ENDIAN
-         val >>= (GET_MODE_BITSIZE (op0_mode) - INTVAL (op2) - INTVAL (op1));
-#else
-         val >>= INTVAL (op2);
-#endif
+         if (BITS_BIG_ENDIAN)
+           val >>= (GET_MODE_BITSIZE (op0_mode)
+                    - INTVAL (op2) - INTVAL (op1));
+         else
+           val >>= INTVAL (op2);
+
          if (HOST_BITS_PER_WIDE_INT != INTVAL (op1))
            {
              /* First zero-extend.  */
@@ -4943,7 +5107,7 @@ fold_rtx (x, insn)
            if (offset == 0 && mode == const_mode)
              return constant;
 
-           /* If this actually isn't a constant (wierd!), we can't do
+           /* If this actually isn't a constant (weird!), we can't do
               anything.  Otherwise, handle the two most common cases:
               extracting a word from a multi-word constant, and extracting
               the low-order bits.  Other cases don't seem common enough to
@@ -5011,6 +5175,12 @@ fold_rtx (x, insn)
 
        return x;
       }
+
+    case ASM_OPERANDS:
+      for (i = XVECLEN (x, 3) - 1; i >= 0; i--)
+       validate_change (insn, &XVECEXP (x, 3, i),
+                        fold_rtx (XVECEXP (x, 3, i), insn), 0);
+      break;
     }
 
   const_arg0 = 0;
@@ -5232,12 +5402,14 @@ fold_rtx (x, insn)
          if (mode_arg0 == VOIDmode || GET_MODE_CLASS (mode_arg0) == MODE_CC)
            break;
 
-         /* If we do not now have two constants being compared, see if we
-            can nevertheless deduce some things about the comparison.  */
+         /* If we do not now have two constants being compared, see
+            if we can nevertheless deduce some things about the
+            comparison.  */
          if (const_arg0 == 0 || const_arg1 == 0)
            {
-             /* Is FOLDED_ARG0 frame-pointer plus a constant?  Or non-explicit
-                constant?  These aren't zero, but we don't know their sign. */
+             /* Is FOLDED_ARG0 frame-pointer plus a constant?  Or
+                non-explicit constant?  These aren't zero, but we
+                don't know their sign.  */
              if (const_arg1 == const0_rtx
                  && (NONZERO_BASE_PLUS_P (folded_arg0)
 #if 0  /* Sad to say, on sysvr4, #pragma weak can make a symbol address
@@ -5409,11 +5581,17 @@ fold_rtx (x, insn)
          /* If second operand is a register equivalent to a negative
             CONST_INT, see if we can find a register equivalent to the
             positive constant.  Make a MINUS if so.  Don't do this for
-            a negative constant since we might then alternate between
+            a non-negative constant since we might then alternate between
             chosing positive and negative constants.  Having the positive
-            constant previously-used is the more common case.  */
-         if (const_arg1 && GET_CODE (const_arg1) == CONST_INT
-             && INTVAL (const_arg1) < 0 && GET_CODE (folded_arg1) == REG)
+            constant previously-used is the more common case.  Be sure
+            the resulting constant is non-negative; if const_arg1 were
+            the smallest negative number this would overflow: depending
+            on the mode, this would either just be the same value (and
+            hence not save anything) or be incorrect.  */
+         if (const_arg1 != 0 && GET_CODE (const_arg1) == CONST_INT
+             && INTVAL (const_arg1) < 0
+             && - INTVAL (const_arg1) >= 0
+             && GET_CODE (folded_arg1) == REG)
            {
              rtx new_const = GEN_INT (- INTVAL (const_arg1));
              struct table_elt *p
@@ -5440,7 +5618,7 @@ fold_rtx (x, insn)
                                 NULL_RTX);
            }
 
-         /* ... fall through ... */
+         /* ... fall through ...  */
 
        from_plus:
        case SMIN:    case SMAX:      case UMIN:    case UMAX:
@@ -5624,16 +5802,14 @@ gen_lowpart_if_possible (mode, x)
       register int offset = 0;
       rtx new;
 
-#if WORDS_BIG_ENDIAN
-      offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD)
-               - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD));
-#endif
-#if BYTES_BIG_ENDIAN
-      /* Adjust the address so that the address-after-the-data
-        is unchanged.  */
-      offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))
-                - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x))));
-#endif
+      if (WORDS_BIG_ENDIAN)
+       offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD)
+                 - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD));
+      if (BYTES_BIG_ENDIAN)
+       /* Adjust the address so that the address-after-the-data is
+          unchanged.  */
+       offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))
+                  - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x))));
       new = gen_rtx (MEM, mode, plus_constant (XEXP (x, 0), offset));
       if (! memory_address_p (mode, XEXP (new, 0)))
        return 0;
@@ -5998,7 +6174,7 @@ cse_insn (insn, in_libcall_block)
     {
       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)));
+          invalidate (SET_DEST (XEXP (tem, 0)), VOIDmode);
     }
 
   if (GET_CODE (x) == SET)
@@ -6022,14 +6198,14 @@ cse_insn (insn, in_libcall_block)
         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. */
+        We need not check the return of apply_change_group; see canon_reg.  */
 
       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));
+         invalidate (SET_DEST (x), VOIDmode);
        }
       else
        n_sets = 1;
@@ -6060,10 +6236,10 @@ cse_insn (insn, in_libcall_block)
 
              if (GET_CODE (clobbered) == REG
                  || GET_CODE (clobbered) == SUBREG)
-               invalidate (clobbered);
+               invalidate (clobbered, VOIDmode);
              else if (GET_CODE (clobbered) == STRICT_LOW_PART
                       || GET_CODE (clobbered) == ZERO_EXTRACT)
-               invalidate (XEXP (clobbered, 0));
+               invalidate (XEXP (clobbered, 0), GET_MODE (clobbered));
            }
        }
            
@@ -6079,7 +6255,7 @@ cse_insn (insn, in_libcall_block)
                  canon_reg (SET_SRC (y), insn);
                  apply_change_group ();
                  fold_rtx (SET_SRC (y), insn);
-                 invalidate (SET_DEST (y));
+                 invalidate (SET_DEST (y), VOIDmode);
                }
              else if (SET_DEST (y) == pc_rtx
                       && GET_CODE (SET_SRC (y)) == LABEL_REF)
@@ -6138,7 +6314,7 @@ cse_insn (insn, in_libcall_block)
   /* 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 destinatation.  */
+     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))
@@ -6160,11 +6336,13 @@ cse_insn (insn, in_libcall_block)
       rtx dest = SET_DEST (sets[i].rtl);
       rtx src = SET_SRC (sets[i].rtl);
       rtx new = canon_reg (src, insn);
+      int insn_code;
 
       if ((GET_CODE (new) == REG && GET_CODE (src) == REG
           && ((REGNO (new) < FIRST_PSEUDO_REGISTER)
               != (REGNO (src) < FIRST_PSEUDO_REGISTER)))
-         || insn_n_dups[recog_memoized (insn)] > 0)
+         || (insn_code = recog_memoized (insn)) < 0
+         || insn_n_dups[insn_code] > 0)
        validate_change (insn, &SET_SRC (sets[i].rtl), new, 1);
       else
        SET_SRC (sets[i].rtl) = new;
@@ -6252,7 +6430,7 @@ cse_insn (insn, in_libcall_block)
 
       /* If this is a STRICT_LOW_PART assignment, src_eqv corresponds to the
         value of the INNER register, not the destination.  So it is not
-        a legal substitution for the source.  But save it for later.  */
+        a valid substitution for the source.  But save it for later.  */
       if (GET_CODE (dest) == STRICT_LOW_PART)
        src_eqv_here = 0;
       else
@@ -6262,6 +6440,13 @@ cse_insn (insn, in_libcall_block)
         simplified result, which may not necessarily be valid.  */
       src_folded = fold_rtx (src, insn);
 
+#if 0
+      /* ??? This caused bad code to be generated for the m68k port with -O2.
+        Suppose src is (CONST_INT -1), and that after truncation src_folded
+        is (CONST_INT 3).  Suppose src_folded is then used for src_const.
+        At the end we will add src and src_const to the same equivalence
+        class.  We now have 3 and -1 on the same equivalence class.  This
+        causes later instructions to be mis-optimized.  */
       /* If storing a constant in a bitfield, pre-truncate the constant
         so we will be able to record it later.  */
       if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT
@@ -6277,6 +6462,7 @@ cse_insn (insn, in_libcall_block)
              = GEN_INT (INTVAL (src) & (((HOST_WIDE_INT) 1
                                          << INTVAL (width)) - 1));
        }
+#endif
 
       /* Compute SRC's hash code, and also notice if it
         should not be recorded at all.  In that case,
@@ -6586,7 +6772,7 @@ cse_insn (insn, in_libcall_block)
          that are when they are equal cost.  Note that we can never
          worsen an insn as the current contents will also succeed.
         If we find an equivalent identical to the destination, use it as best,
-        since this insn will probably be eliminated in that case. */
+        since this insn will probably be eliminated in that case.  */
       if (src)
        {
          if (rtx_equal_p (src, dest))
@@ -6716,7 +6902,11 @@ cse_insn (insn, in_libcall_block)
 
          else if (constant_pool_entries_cost
                   && CONSTANT_P (trial)
-                  && (src_folded == 0 || GET_CODE (src_folded) != MEM)
+                  && ! (GET_CODE (trial) == CONST
+                        && GET_CODE (XEXP (trial, 0)) == TRUNCATE)
+                  && (src_folded == 0
+                      || (GET_CODE (src_folded) != MEM
+                          && ! src_folded_force_flag))
                   && GET_MODE_CLASS (mode) != MODE_CC)
            {
              src_folded_force_flag = 1;
@@ -6949,7 +7139,7 @@ cse_insn (insn, in_libcall_block)
 
          if (NEXT_INSN (insn) == 0
              || GET_CODE (NEXT_INSN (insn)) != BARRIER)
-           emit_barrier_after (insn);
+           emit_barrier_before (NEXT_INSN (insn));
 
          /* We might have two BARRIERs separated by notes.  Delete the second
             one if so.  */
@@ -6969,10 +7159,10 @@ cse_insn (insn, in_libcall_block)
        {
          if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG
              || GET_CODE (dest) == MEM)
-           invalidate (dest);
+           invalidate (dest, VOIDmode);
          else if (GET_CODE (dest) == STRICT_LOW_PART
                   || GET_CODE (dest) == ZERO_EXTRACT)
-           invalidate (XEXP (dest, 0));
+           invalidate (XEXP (dest, 0), GET_MODE (dest));
          sets[i].rtl = 0;
        }
 
@@ -7018,7 +7208,10 @@ cse_insn (insn, in_libcall_block)
          classp = 0;
        }
       if (insert_regs (src_eqv, classp, 0))
-       src_eqv_hash = HASH (src_eqv, eqvmode);
+       {
+         rehash_using_reg (src_eqv);
+         src_eqv_hash = HASH (src_eqv, eqvmode);
+       }
       elt = insert (src_eqv, classp, src_eqv_hash, eqvmode);
       elt->in_memory = src_eqv_in_memory;
       elt->in_struct = src_eqv_in_struct;
@@ -7065,7 +7258,10 @@ cse_insn (insn, in_libcall_block)
                   any of the src_elt's, because they would have failed to
                   match if not still valid.  */
                if (insert_regs (src, classp, 0))
-                 sets[i].src_hash = HASH (src, mode);
+                 {
+                   rehash_using_reg (src);
+                   sets[i].src_hash = HASH (src, mode);
+                 }
                elt = insert (src, classp, sets[i].src_hash, mode);
                elt->in_memory = sets[i].src_in_memory;
                elt->in_struct = sets[i].src_in_struct;
@@ -7107,18 +7303,21 @@ cse_insn (insn, in_libcall_block)
   for (i = 0; i < n_sets; i++)
     if (sets[i].rtl)
       {
-       register rtx dest = sets[i].inner_dest;
+       /* We can't use the inner dest, because the mode associated with
+          a ZERO_EXTRACT is significant.  */
+       register rtx dest = SET_DEST (sets[i].rtl);
 
        /* Needed for registers to remove the register from its
           previous quantity's chain.
           Needed for memory if this is a nonvarying address, unless
           we have just done an invalidate_memory that covers even those.  */
        if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG
-           || (! writes_memory.all && ! cse_rtx_addr_varies_p (dest)))
-         invalidate (dest);
+           || (GET_CODE (dest) == MEM && ! writes_memory.all
+               && ! cse_rtx_addr_varies_p (dest)))
+         invalidate (dest, VOIDmode);
        else if (GET_CODE (dest) == STRICT_LOW_PART
                 || GET_CODE (dest) == ZERO_EXTRACT)
-         invalidate (XEXP (dest, 0));
+         invalidate (XEXP (dest, 0), GET_MODE (dest));
       }
 
   /* Make sure registers mentioned in destinations
@@ -7175,7 +7374,17 @@ cse_insn (insn, in_libcall_block)
            || in_libcall_block
            /* If we didn't put a REG_EQUAL value or a source into the hash
               table, there is no point is recording DEST.  */
-            || sets[i].src_elt == 0)
+           || sets[i].src_elt == 0
+           /* If DEST is a paradoxical SUBREG and SRC is a ZERO_EXTEND
+              or SIGN_EXTEND, don't record DEST since it can cause
+              some tracking to be wrong.
+
+              ??? Think about this more later.  */
+           || (GET_CODE (dest) == SUBREG
+               && (GET_MODE_SIZE (GET_MODE (dest))
+                   > GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))))
+               && (GET_CODE (sets[i].src) == SIGN_EXTEND
+                   || GET_CODE (sets[i].src) == ZERO_EXTEND)))
          continue;
 
        /* STRICT_LOW_PART isn't part of the value BEING set,
@@ -7187,13 +7396,20 @@ cse_insn (insn, in_libcall_block)
        if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG)
          /* Registers must also be inserted into chains for quantities.  */
          if (insert_regs (dest, sets[i].src_elt, 1))
-           /* If `insert_regs' changes something, the hash code must be
-              recalculated.  */
-           sets[i].dest_hash = HASH (dest, GET_MODE (dest));
+           {
+             /* If `insert_regs' changes something, the hash code must be
+                recalculated.  */
+             rehash_using_reg (dest);
+             sets[i].dest_hash = HASH (dest, GET_MODE (dest));
+           }
 
        elt = insert (dest, sets[i].src_elt,
                      sets[i].dest_hash, GET_MODE (dest));
-       elt->in_memory = GET_CODE (sets[i].inner_dest) == MEM;
+       elt->in_memory = (GET_CODE (sets[i].inner_dest) == MEM
+                         && (! RTX_UNCHANGING_P (sets[i].inner_dest)
+                             || FIXED_BASE_PLUS_P (XEXP (sets[i].inner_dest,
+                                                         0))));
+
        if (elt->in_memory)
          {
            /* This implicitly assumes a whole struct
@@ -7253,7 +7469,10 @@ cse_insn (insn, in_libcall_block)
                if (src_elt == 0)
                  {
                    if (insert_regs (new_src, classp, 0))
-                     src_hash = HASH (new_src, new_mode);
+                     {
+                       rehash_using_reg (new_src);
+                       src_hash = HASH (new_src, new_mode);
+                     }
                    src_elt = insert (new_src, classp, src_hash, new_mode);
                    src_elt->in_memory = elt->in_memory;
                    src_elt->in_struct = elt->in_struct;
@@ -7323,6 +7542,12 @@ cse_insn (insn, in_libcall_block)
              XEXP (note, 1) = REG_NOTES (prev);
              REG_NOTES (prev) = note;
            }
+
+         /* If INSN has a REG_EQUAL note, and this note mentions REG0,
+            then we must delete it, because the value in REG0 has changed.  */
+         note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
+         if (note && reg_mentioned_p (dest, XEXP (note, 0)))
+           remove_note (insn, note);
        }
     }
 
@@ -7371,7 +7596,7 @@ note_mem_written (written, writes_ptr)
     *writes_ptr = everything;
   else if (GET_CODE (written) == MEM)
     {
-      /* Pushing or popping the stack invalidates just the stack pointer. */
+      /* Pushing or popping the stack invalidates just the stack pointer.  */
       rtx addr = XEXP (written, 0);
       if ((GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == PRE_INC
           || GET_CODE (addr) == POST_DEC || GET_CODE (addr) == POST_INC)
@@ -7383,19 +7608,21 @@ note_mem_written (written, writes_ptr)
        }
       else if (GET_MODE (written) == BLKmode)
        *writes_ptr = everything;
-      /* (mem (scratch)) means clobber everything.  */
-      else if (GET_CODE (addr) == SCRATCH)
-       *writes_ptr = everything;
       else if (cse_rtx_addr_varies_p (written))
        {
          /* A varying address that is a sum indicates an array element,
             and that's just as good as a structure element
             in implying that we need not invalidate scalar variables.
             However, we must allow QImode aliasing of scalars, because the
-            ANSI C standard allows character pointers to alias anything.  */
+            ANSI C standard allows character pointers to alias anything.
+            We must also allow AND addresses, because they may generate
+            accesses outside the object being referenced.  This is used to
+            generate aligned addresses from unaligned addresses, for instance,
+            the alpha storeqi_unaligned pattern.  */
          if (! ((MEM_IN_STRUCT_P (written)
                  || GET_CODE (XEXP (written, 0)) == PLUS)
-                && GET_MODE (written) != QImode))
+                && GET_MODE (written) != QImode
+                && GET_CODE (XEXP (written, 0)) != AND))
            writes_ptr->all = 1;
          writes_ptr->nonscalar = 1;
        }
@@ -7430,21 +7657,19 @@ invalidate_from_clobbers (w, x)
 
       /* This should be *very* rare.  */
       if (TEST_HARD_REG_BIT (hard_regs_in_table, STACK_POINTER_REGNUM))
-       invalidate (stack_pointer_rtx);
+       invalidate (stack_pointer_rtx, VOIDmode);
     }
 
   if (GET_CODE (x) == CLOBBER)
     {
       rtx ref = XEXP (x, 0);
-      if (ref)
-       {
-         if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
-             || (GET_CODE (ref) == MEM && ! w->all))
-           invalidate (ref);
-         else if (GET_CODE (ref) == STRICT_LOW_PART
-                  || GET_CODE (ref) == ZERO_EXTRACT)
-           invalidate (XEXP (ref, 0));
-       }
+
+      if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
+         || (GET_CODE (ref) == MEM && ! w->all))
+       invalidate (ref, VOIDmode);
+      else if (GET_CODE (ref) == STRICT_LOW_PART
+              || GET_CODE (ref) == ZERO_EXTRACT)
+       invalidate (XEXP (ref, 0), GET_MODE (ref));
     }
   else if (GET_CODE (x) == PARALLEL)
     {
@@ -7459,10 +7684,10 @@ invalidate_from_clobbers (w, x)
                {
                  if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
                      || (GET_CODE (ref) == MEM && !w->all))
-                   invalidate (ref);
+                   invalidate (ref, VOIDmode);
                  else if (GET_CODE (ref) == STRICT_LOW_PART
                           || GET_CODE (ref) == ZERO_EXTRACT)
-                   invalidate (XEXP (ref, 0));
+                   invalidate (XEXP (ref, 0), GET_MODE (ref));
                }
            }
        }
@@ -7513,6 +7738,7 @@ cse_process_notes (x, object)
 
     case SIGN_EXTEND:
     case ZERO_EXTEND:
+    case SUBREG:
       {
        rtx new = cse_process_notes (XEXP (x, 0), object);
        /* We don't substitute VOIDmode constants into these rtx,
@@ -7590,11 +7816,12 @@ cse_around_loop (loop_start)
     for (p = last_jump_equiv_class->first_same_value; p;
         p = p->next_same_value)
       if (GET_CODE (p->exp) == MEM || GET_CODE (p->exp) == REG
-         || GET_CODE (p->exp) == SUBREG)
-       invalidate (p->exp);
+         || (GET_CODE (p->exp) == SUBREG
+             && GET_CODE (SUBREG_REG (p->exp)) == REG))
+       invalidate (p->exp, VOIDmode);
       else if (GET_CODE (p->exp) == STRICT_LOW_PART
               || GET_CODE (p->exp) == ZERO_EXTRACT)
-       invalidate (XEXP (p->exp, 0));
+       invalidate (XEXP (p->exp, 0), GET_MODE (p->exp));
 
   /* Process insns starting after LOOP_START until we hit a CALL_INSN or
      a CODE_LABEL (we could handle a CALL_INSN, but it isn't worth it).
@@ -7635,13 +7862,6 @@ invalidate_skipped_set (dest, set)
      rtx set;
      rtx dest;
 {
-  if (GET_CODE (set) == CLOBBER
-#ifdef HAVE_cc0
-      || dest == cc0_rtx
-#endif
-      || dest == pc_rtx)
-    return;
-
   if (GET_CODE (dest) == MEM)
     note_mem_written (dest, &skipped_writes_memory);
 
@@ -7651,12 +7871,19 @@ invalidate_skipped_set (dest, set)
   if (skipped_writes_memory.nonscalar)
     skipped_writes_memory.all = 1;
 
+  if (GET_CODE (set) == CLOBBER
+#ifdef HAVE_cc0
+      || dest == cc0_rtx
+#endif
+      || dest == pc_rtx)
+    return;
+
   if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG
       || (! skipped_writes_memory.all && ! cse_rtx_addr_varies_p (dest)))
-    invalidate (dest);
+    invalidate (dest, VOIDmode);
   else if (GET_CODE (dest) == STRICT_LOW_PART
           || GET_CODE (dest) == ZERO_EXTRACT)
-    invalidate (XEXP (dest, 0));
+    invalidate (XEXP (dest, 0), GET_MODE (dest));
 }
 
 /* Invalidate all insns from START up to the end of the function or the
@@ -7804,14 +8031,15 @@ cse_set_around_loop (x, insn, loop_start)
   if (writes_memory.var)
     invalidate_memory (&writes_memory);
 
-  /* See comment on similar code in cse_insn for explanation of these tests. */
+  /* See comment on similar code in cse_insn for explanation of these
+     tests.  */
   if (GET_CODE (SET_DEST (x)) == REG || GET_CODE (SET_DEST (x)) == SUBREG
       || (GET_CODE (SET_DEST (x)) == MEM && ! writes_memory.all
          && ! cse_rtx_addr_varies_p (SET_DEST (x))))
-    invalidate (SET_DEST (x));
+    invalidate (SET_DEST (x), VOIDmode);
   else if (GET_CODE (SET_DEST (x)) == STRICT_LOW_PART
           || GET_CODE (SET_DEST (x)) == ZERO_EXTRACT)
-    invalidate (XEXP (SET_DEST (x), 0));
+    invalidate (XEXP (SET_DEST (x), 0), GET_MODE (SET_DEST (x)));
 }
 \f
 /* Find the end of INSN's basic block and return its range,
@@ -8052,6 +8280,7 @@ cse_main (f, nregs, after_loop, file)
   register int i;
 
   cse_jumps_altered = 0;
+  recorded_label_ref = 0;
   constant_pool_entries_cost = 0;
   val.path_size = 0;
 
@@ -8207,7 +8436,7 @@ cse_main (f, nregs, after_loop, file)
   if (max_elements_made < n_elements_made)
     max_elements_made = n_elements_made;
 
-  return cse_jumps_altered;
+  return cse_jumps_altered || recorded_label_ref;
 }
 
 /* Process a single basic block.  FROM and TO and the limits of the basic
@@ -8343,22 +8572,25 @@ cse_basic_block (from, to, next_branch, around_loop)
          && GET_CODE (to) == CODE_LABEL && --LABEL_NUSES (to) == to_usage)
        {
          struct cse_basic_block_data val;
+         rtx prev;
 
          insn = NEXT_INSN (to);
 
          if (LABEL_NUSES (to) == 0)
-           delete_insn (to);
-
-         /* Find the end of the following block.  Note that we won't be
-            following branches in this case.  If TO was the last insn
-            in the function, we are done.  Similarly, if we deleted the
-            insn after TO, it must have been because it was preceded by
-            a BARRIER.  In that case, we are done with this block because it
-            has no continuation.  */
+           insn = delete_insn (to);
 
-         if (insn == 0 || INSN_DELETED_P (insn))
+         /* If TO was the last insn in the function, we are done.  */
+         if (insn == 0)
            return 0;
 
+         /* If TO was preceded by a BARRIER we are done with this block
+            because it has no continuation.  */
+         prev = prev_nonnote_insn (to);
+         if (prev && GET_CODE (prev) == BARRIER)
+           return insn;
+
+         /* Find the end of the following block.  Note that we won't be
+            following branches in this case.  */
          to_usage = 0;
          val.path_size = 0;
          cse_end_of_basic_block (insn, &val, 0, 0, 0);