OSDN Git Service

(fold_rtx, case PLUS): When seeing if negative of constant is around,
[pf3gnuchains/gcc-fork.git] / gcc / cse.c
index 2f37876..260da29 100644 (file)
--- a/gcc/cse.c
+++ b/gcc/cse.c
@@ -1,5 +1,5 @@
 /* Common subexpression elimination for GNU compiler.
-   Copyright (C) 1987, 1988, 1989, 1992, 1993 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,12 +343,23 @@ 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.  */
 
 static int do_not_record;
 
+#ifdef LOAD_EXTEND_OP
+
+/* Scratch rtl used when looking for load-extended copy of a MEM.  */
+static rtx memory_extend_rtx;
+#endif
+
 /* canon_hash stores 1 in hash_arg_in_memory
    if it notices a reference to memory within the expression being hashed.  */
 
@@ -418,8 +430,6 @@ struct table_elt
   char flag;
 };
 
-#define HASHBITS 16
-
 /* We don't want a lot of buckets, because we rarely have very many
    things stored in the hash table, and a lot of buckets slows
    down a lot of loops that happen frequently.  */
@@ -430,7 +440,7 @@ struct table_elt
 
 #define HASH(X, M)     \
  (GET_CODE (X) == REG && REGNO (X) >= FIRST_PSEUDO_REGISTER    \
-  ? ((((int) REG << 7) + reg_qty[REGNO (X)]) % NBUCKETS)       \
+  ? (((unsigned) REG << 7) + (unsigned) reg_qty[REGNO (X)]) % NBUCKETS \
   : canon_hash (X, M) % NBUCKETS)
 
 /* Determine whether register number N is considered a fixed register for CSE.
@@ -441,12 +451,12 @@ struct table_elt
 #ifdef OVERLAPPING_REGNO_P
 #define FIXED_REGNO_P(N)  \
   (((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
-    || fixed_regs[N])    \
+    || fixed_regs[N] || global_regs[N])          \
    && ! OVERLAPPING_REGNO_P ((N)))
 #else
 #define FIXED_REGNO_P(N)  \
   ((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
-   || fixed_regs[N])
+   || fixed_regs[N] || global_regs[N])
 #endif
 
 /* Compute cost of X, as stored in the `cost' field of a table_elt.  Fixed
@@ -454,19 +464,38 @@ struct table_elt
    of 0.  Next come pseudos with a cost of one and other hard registers with
    a cost of 2.  Aside from these special cases, call `rtx_cost'.  */
 
-#define CHEAP_REG(N) \
+#define CHEAP_REGNO(N) \
   ((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM     \
    || (N) == STACK_POINTER_REGNUM || (N) == ARG_POINTER_REGNUM         \
    || ((N) >= FIRST_VIRTUAL_REGISTER && (N) <= LAST_VIRTUAL_REGISTER)  \
    || ((N) < FIRST_PSEUDO_REGISTER                                     \
        && FIXED_REGNO_P (N) && REGNO_REG_CLASS (N) != NO_REGS))
 
-#define COST(X)                                                \
-  (GET_CODE (X) == REG                                 \
-   ? (CHEAP_REG (REGNO (X)) ? 0                                \
-      : REGNO (X) >= FIRST_PSEUDO_REGISTER ? 1         \
-      : 2)                                             \
-   : rtx_cost (X, SET) * 2)
+/* A register is cheap if it is a user variable assigned to the register
+   or if its register number always corresponds to a cheap register.  */
+
+#define CHEAP_REG(N) \
+  ((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)                                                             \
+   : ((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.  */
@@ -517,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.  */
@@ -542,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
@@ -598,23 +627,23 @@ static void delete_reg_equiv      PROTO((int));
 static int mention_regs                PROTO((rtx));
 static int insert_regs         PROTO((rtx, struct table_elt *, int));
 static void free_element       PROTO((struct table_elt *));
-static void remove_from_table  PROTO((struct table_elt *, int));
+static void remove_from_table  PROTO((struct table_elt *, unsigned));
 static struct table_elt *get_element PROTO((void));
-static struct table_elt *lookup        PROTO((rtx, int, enum machine_mode)),
-       *lookup_for_remove PROTO((rtx, int, enum machine_mode));
+static struct table_elt *lookup        PROTO((rtx, unsigned, enum machine_mode)),
+       *lookup_for_remove PROTO((rtx, unsigned, enum machine_mode));
 static rtx lookup_as_function  PROTO((rtx, enum rtx_code));
-static struct table_elt *insert PROTO((rtx, struct table_elt *, int,
+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 *));
 static void invalidate_for_call        PROTO((void));
 static rtx use_related_value   PROTO((rtx, struct table_elt *));
-static int canon_hash          PROTO((rtx, enum machine_mode));
-static int safe_hash           PROTO((rtx, enum machine_mode));
+static unsigned canon_hash     PROTO((rtx, enum machine_mode));
+static unsigned safe_hash      PROTO((rtx, enum machine_mode));
 static int exp_equiv_p         PROTO((rtx, rtx, int, int));
 static void set_nonvarying_address_components PROTO((rtx, int, rtx *,
                                                     HOST_WIDE_INT *,
@@ -647,7 +676,9 @@ static void invalidate_skipped_block PROTO((rtx));
 static void cse_check_loop_start PROTO((rtx, rtx));
 static void cse_set_around_loop        PROTO((rtx, rtx, rtx));
 static rtx cse_basic_block     PROTO((rtx, rtx, struct branch_path *, int));
-static void count_reg_usage    PROTO((rtx, int *, int));
+static void count_reg_usage    PROTO((rtx, int *, rtx, int));
+
+extern int rtx_equal_function_value_matters;
 \f
 /* Return an estimate of the cost of computing rtx X.
    One use is in cse, to decide which expression to keep in the hash table.
@@ -711,7 +742,7 @@ rtx_cost (x, outer_code)
   switch (code)
     {
     case REG:
-      return ! CHEAP_REG (REGNO (x));
+      return ! CHEAP_REG (x);
 
     case SUBREG:
       /* If we can't tie these modes, make this expensive.  The larger
@@ -750,10 +781,11 @@ new_basic_block ()
 
   next_qty = max_reg;
 
-  bzero (reg_tick, max_reg * sizeof (int));
+  bzero ((char *) reg_tick, max_reg * sizeof (int));
 
-  bcopy (all_minus_one, reg_in_table, max_reg * sizeof (int));
-  bcopy (consec_ints, reg_qty, max_reg * sizeof (int));
+  bcopy ((char *) all_minus_one, (char *) reg_in_table,
+        max_reg * sizeof (int));
+  bcopy ((char *) consec_ints, (char *) reg_qty, max_reg * sizeof (int));
   CLEAR_HARD_REG_SET (hard_regs_in_table);
 
   /* The per-quantity values used to be initialized here, but it is
@@ -769,7 +801,7 @@ new_basic_block ()
        }
     }
 
-  bzero (table, sizeof table);
+  bzero ((char *) table, sizeof table);
 
   prev_insn = 0;
 
@@ -867,14 +899,16 @@ static void
 delete_reg_equiv (reg)
      register int reg;
 {
-  register int n = reg_next_eqv[reg];
-  register int p = reg_prev_eqv[reg];
   register int q = reg_qty[reg];
+  register int p, n;
 
-  /* If invalid, do nothing.  N and P above are undefined in that case.  */
+  /* If invalid, do nothing.  */
   if (q == reg)
     return;
 
+  p = reg_prev_eqv[reg];
+  n = reg_next_eqv[reg];
+
   if (n != -1)
     reg_prev_eqv[n] = p;
   else
@@ -1071,7 +1105,7 @@ get_element ()
 static void
 remove_from_table (elt, hash)
      register struct table_elt *elt;
-     int hash;
+     unsigned hash;
 {
   if (elt == 0)
     return;
@@ -1151,7 +1185,7 @@ remove_from_table (elt, hash)
 static struct table_elt *
 lookup (x, hash, mode)
      rtx x;
-     int hash;
+     unsigned hash;
      enum machine_mode mode;
 {
   register struct table_elt *p;
@@ -1170,7 +1204,7 @@ lookup (x, hash, mode)
 static struct table_elt *
 lookup_for_remove (x, hash, mode)
      rtx x;
-     int hash;
+     unsigned hash;
      enum machine_mode mode;
 {
   register struct table_elt *p;
@@ -1249,7 +1283,7 @@ static struct table_elt *
 insert (x, classp, hash, mode)
      register rtx x;
      register struct table_elt *classp;
-     int hash;
+     unsigned hash;
      enum machine_mode mode;
 {
   register struct table_elt *elt;
@@ -1270,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.  */
 
@@ -1344,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);
@@ -1377,7 +1418,7 @@ insert (x, classp, hash, mode)
   if (GET_CODE (x) == CONST)
     {
       rtx subexp = get_related_value (x);
-      int subhash;
+      unsigned subhash;
       struct table_elt *subelt, *subelt_prev;
 
       if (subexp != 0)
@@ -1429,7 +1470,7 @@ merge_equiv_classes (class1, class2)
 
   for (elt = class2; elt; elt = next)
     {
-      int hash;
+      unsigned hash;
       rtx exp = elt->exp;
       enum machine_mode mode = elt->mode;
 
@@ -1437,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;
@@ -1450,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;
@@ -1464,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;
@@ -1487,7 +1535,7 @@ invalidate (x)
   if (GET_CODE (x) == REG)
     {
       register int regno = REGNO (x);
-      register int hash = HASH (x, GET_MODE (x));
+      register unsigned hash = HASH (x, GET_MODE (x));
 
       /* Remove REGNO from any quantity list it might be on and indicate
         that it's value might have changed.  If it is a pseudo, remove its
@@ -1502,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
@@ -1546,7 +1602,7 @@ invalidate (x)
     {
       if (GET_CODE (SUBREG_REG (x)) != REG)
        abort ();
-      invalidate (SUBREG_REG (x));
+      invalidate (SUBREG_REG (x), VOIDmode);
       return;
     }
 
@@ -1557,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++)
@@ -1605,7 +1664,7 @@ rehash_using_reg (x)
 {
   int i;
   struct table_elt *p, *next;
-  int hash;
+  unsigned hash;
 
   if (GET_CODE (x) == SUBREG)
     x = SUBREG_REG (x);
@@ -1679,7 +1738,7 @@ invalidate_for_call ()
 {
   int regno, endregno;
   int i;
-  int hash;
+  unsigned hash;
   struct table_elt *p, *next;
   int in_table = 0;
 
@@ -1695,7 +1754,7 @@ invalidate_for_call ()
        if (reg_tick[regno] >= 0)
          reg_tick[regno]++;
 
-       in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, regno);
+       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
@@ -1814,13 +1873,13 @@ use_related_value (x, elt)
    Note that cse_insn knows that the hash code of a MEM expression
    is just (int) MEM plus the hash code of the address.  */
 
-static int
+static unsigned
 canon_hash (x, mode)
      rtx x;
      enum machine_mode mode;
 {
   register int i, j;
-  register int hash = 0;
+  register unsigned hash = 0;
   register enum rtx_code code;
   register char *fmt;
 
@@ -1839,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
@@ -1855,37 +1915,42 @@ canon_hash (x, mode)
            do_not_record = 1;
            return 0;
          }
-       return hash + ((int) REG << 7) + reg_qty[regno];
+       hash += ((unsigned) REG << 7) + (unsigned) reg_qty[regno];
+       return hash;
       }
 
     case CONST_INT:
-      hash += ((int) mode + ((int) CONST_INT << 7)
-              + INTVAL (x) + (INTVAL (x) >> HASHBITS));
-      return ((1 << HASHBITS) - 1) & hash;
+      {
+       unsigned HOST_WIDE_INT tem = INTVAL (x);
+       hash += ((unsigned) CONST_INT << 7) + (unsigned) mode + tem;
+       return hash;
+      }
 
     case CONST_DOUBLE:
       /* This is like the general case, except that it only counts
         the integers representing the constant.  */
-      hash += (int) code + (int) GET_MODE (x);
-      {
-       int i;
+      hash += (unsigned) code + (unsigned) GET_MODE (x);
+      if (GET_MODE (x) != VOIDmode)
        for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++)
          {
-           int tem = XINT (x, i);
-           hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS));
+           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.  */
     case LABEL_REF:
-      /* Use `and' to ensure a positive number.  */
-      return (hash + ((HOST_WIDE_INT) LABEL_REF << 7)
-             + ((HOST_WIDE_INT) XEXP (x, 0) & ((1 << HASHBITS) - 1)));
+      hash
+       += ((unsigned) LABEL_REF << 7) + (unsigned HOST_WIDE_INT) XEXP (x, 0);
+      return hash;
 
     case SYMBOL_REF:
-      return (hash + ((HOST_WIDE_INT) SYMBOL_REF << 7)
-             + ((HOST_WIDE_INT) XEXP (x, 0) & ((1 << HASHBITS) - 1)));
+      hash
+       += ((unsigned) SYMBOL_REF << 7) + (unsigned HOST_WIDE_INT) XSTR (x, 0);
+      return hash;
 
     case MEM:
       if (MEM_VOLATILE_P (x))
@@ -1893,14 +1958,14 @@ 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;
        }
       /* Now that we have already found this special case,
         might as well speed it up as much as possible.  */
-      hash += (int) MEM;
+      hash += (unsigned) MEM;
       x = XEXP (x, 0);
       goto repeat;
 
@@ -1924,24 +1989,13 @@ canon_hash (x, mode)
     }
 
   i = GET_RTX_LENGTH (code) - 1;
-  hash += (int) code + (int) GET_MODE (x);
+  hash += (unsigned) code + (unsigned) GET_MODE (x);
   fmt = GET_RTX_FORMAT (code);
   for (; i >= 0; i--)
     {
       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.
@@ -1958,18 +2012,15 @@ canon_hash (x, mode)
          hash += canon_hash (XVECEXP (x, i, j), 0);
       else if (fmt[i] == 's')
        {
-         register char *p = XSTR (x, i);
+         register unsigned char *p = (unsigned char *) XSTR (x, i);
          if (p)
            while (*p)
-             {
-               register int tem = *p++;
-               hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS));
-             }
+             hash += *p++;
        }
       else if (fmt[i] == 'i')
        {
-         register int tem = XINT (x, i);
-         hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS));
+         register unsigned tem = XINT (x, i);
+         hash += tem;
        }
       else
        abort ();
@@ -1979,7 +2030,7 @@ canon_hash (x, mode)
 
 /* Like canon_hash but with no side effects.  */
 
-static int
+static unsigned
 safe_hash (x, mode)
      rtx x;
      enum machine_mode mode;
@@ -1987,7 +2038,7 @@ safe_hash (x, mode)
   int save_do_not_record = do_not_record;
   int save_hash_arg_in_memory = hash_arg_in_memory;
   int save_hash_arg_in_struct = hash_arg_in_struct;
-  int hash = canon_hash (x, mode);
+  unsigned hash = canon_hash (x, mode);
   hash_arg_in_memory = save_hash_arg_in_memory;
   hash_arg_in_struct = save_hash_arg_in_struct;
   do_not_record = save_do_not_record;
@@ -2064,9 +2115,11 @@ exp_equiv_p (x, y, validate, equal_values)
       return INTVAL (x) == INTVAL (y);
 
     case LABEL_REF:
-    case SYMBOL_REF:
       return XEXP (x, 0) == XEXP (y, 0);
 
+    case SYMBOL_REF:
+      return XSTR (x, 0) == XSTR (y, 0);
+
     case REG:
       {
        int regno = REGNO (y);
@@ -2214,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)
@@ -2226,7 +2280,7 @@ set_nonvarying_address_components (addr, size, pbase, pstart, pend)
      HOST_WIDE_INT *pstart, *pend;
 {
   rtx base;
-  int start, end;
+  HOST_WIDE_INT start, end;
 
   base = addr;
   start = 0;
@@ -2252,21 +2306,97 @@ 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.  */
+
+  while (1)
+    {
+      /* If no part of this switch does a "continue", the code outside
+        will exit this loop.  */
+
+      switch (GET_CODE (base))
+       {
+       case LO_SUM:
+         /* By definition, operand1 of a LO_SUM is the associated constant
+            address.  Use the associated constant address as the base
+            instead.  */
+         base = XEXP (base, 1);
+         continue;
+
+       case CONST:
+         /* Strip off CONST.  */
+         base = XEXP (base, 0);
+         continue;
+
+       case PLUS:
+         if (GET_CODE (XEXP (base, 1)) == CONST_INT)
+           {
+             start += INTVAL (XEXP (base, 1));
+             base = XEXP (base, 0);
+             continue;
+           }
+         break;
+
+       case AND:
+         /* Handle the case of an AND which is the negative of a power of
+            two.  This is used to represent unaligned memory operations.  */
+         if (GET_CODE (XEXP (base, 1)) == CONST_INT
+             && exact_log2 (- INTVAL (XEXP (base, 1))) > 0)
+           {
+             set_nonvarying_address_components (XEXP (base, 0), size,
+                                                pbase, pstart, pend);
+
+             /* Assume the worst misalignment.  START is affected, but not
+                END, so compensate but adjusting SIZE.  Don't lose any
+                constant we already had.  */
 
-  /* By definition, operand1 of a LO_SUM is the associated constant
-     address.  Use the associated constant address as the base instead.  */
-  if (GET_CODE (base) == LO_SUM)
-    base = XEXP (base, 1);
+             size = *pend - *pstart - INTVAL (XEXP (base, 1)) - 1;
+             start += *pstart + INTVAL (XEXP (base, 1)) + 1;
+             end += *pend;
+             base = *pbase;
+           }
+         break;
+       }
 
-  /* Strip off CONST.  */
-  if (GET_CODE (base) == CONST)
-    base = XEXP (base, 0);
+      break;
+    }
 
-  if (GET_CODE (base) == PLUS
-      && GET_CODE (XEXP (base, 1)) == CONST_INT)
+  if (GET_CODE (base) == CONST_INT)
     {
-      start += INTVAL (XEXP (base, 1));
-      base = XEXP (base, 0);
+      start += INTVAL (base);
+      base = const0_rtx;
     }
 
   end = start + size;
@@ -2299,13 +2429,6 @@ refers_to_mem_p (x, base, start, end)
   register enum rtx_code code;
   register char *fmt;
 
-  if (GET_CODE (base) == CONST_INT)
-    {
-      start += INTVAL (base);
-      end += INTVAL (base);
-      base = const0_rtx;
-    }
-
  repeat:
   if (x == 0)
     return 0;
@@ -2385,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
@@ -2453,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.  */
@@ -2460,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;
@@ -2473,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.
 
@@ -2501,9 +2645,9 @@ find_best_addr (insn, loc)
   int save_do_not_record = do_not_record;
   int save_hash_arg_in_memory = hash_arg_in_memory;
   int save_hash_arg_in_struct = hash_arg_in_struct;
-  int hash_code;
   int addr_volatile;
   int regno;
+  unsigned hash;
 
   /* Do not try to replace constant addresses or addresses of local and
      argument slots.  These MEM expressions are made only once and inserted
@@ -2531,15 +2675,27 @@ 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.  */
 
   do_not_record = 0;
-  hash_code = HASH (addr, Pmode);
+  hash = HASH (addr, Pmode);
   addr_volatile = do_not_record;
   do_not_record = save_do_not_record;
   hash_arg_in_memory = save_hash_arg_in_memory;
@@ -2548,7 +2704,7 @@ find_best_addr (insn, loc)
   if (addr_volatile)
     return;
 
-  elt = lookup (addr, hash_code, Pmode);
+  elt = lookup (addr, hash, Pmode);
 
 #ifndef ADDRESS_COST
   if (elt)
@@ -2627,12 +2783,12 @@ find_best_addr (insn, loc)
       rtx c = XEXP (*loc, 1);
 
       do_not_record = 0;
-      hash_code = HASH (XEXP (*loc, 0), Pmode);
+      hash = HASH (XEXP (*loc, 0), Pmode);
       do_not_record = save_do_not_record;
       hash_arg_in_memory = save_hash_arg_in_memory;
       hash_arg_in_struct = save_hash_arg_in_struct;
 
-      elt = lookup (XEXP (*loc, 0), hash_code, Pmode);
+      elt = lookup (XEXP (*loc, 0), hash, Pmode);
       if (elt == 0)
        return;
 
@@ -2650,9 +2806,17 @@ find_best_addr (insn, loc)
          int best_rtx_cost = (COST (*loc) + 1) >> 1;
          struct table_elt *best_elt = elt; 
          rtx best_rtx = *loc;
+         int count;
+
+         /* This is at worst case an O(n^2) algorithm, so limit our search
+            to the first 32 elements on the list.  This avoids trouble
+            compiling code with very long basic blocks that can easily
+            call cse_gen_binary so many times that we run out of memory.  */
 
          found_better = 0;
-         for (p = elt->first_same_value; p; p = p->next_same_value)
+         for (p = elt->first_same_value, count = 0;
+              p && count < 32;
+              p = p->next_same_value, count++)
            if (! p->flag
                && (GET_CODE (p->exp) == REG
                    || exp_equiv_p (p->exp, p->exp, 1, 0)))
@@ -2865,70 +3029,74 @@ simplify_unary_operation (code, mode, op, op_mode)
      check the wrong mode (input vs. output) for a conversion operation,
      such as FIX.  At some point, this should be simplified.  */
 
-#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
-  if (code == FLOAT && GET_CODE (op) == CONST_INT)
-    {
-      REAL_VALUE_TYPE d;
+#if !defined(REAL_IS_NOT_DOUBLE) || defined(REAL_ARITHMETIC)
 
-#ifdef REAL_ARITHMETIC
-      REAL_VALUE_FROM_INT (d, INTVAL (op), INTVAL (op) < 0 ? ~0 : 0);
-#else
-      d = (double) INTVAL (op);
-#endif
-      return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
-    }
-  else if (code == UNSIGNED_FLOAT && GET_CODE (op) == CONST_INT)
+  if (code == FLOAT && GET_MODE (op) == VOIDmode
+      && (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT))
     {
+      HOST_WIDE_INT hv, lv;
       REAL_VALUE_TYPE d;
 
-#ifdef REAL_ARITHMETIC
-      REAL_VALUE_FROM_INT (d, INTVAL (op), 0);
-#else
-      d = (double) (unsigned int) INTVAL (op);
-#endif
-      return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
-    }
-
-  else if (code == FLOAT && GET_CODE (op) == CONST_DOUBLE
-          && GET_MODE (op) == VOIDmode)
-    {
-      REAL_VALUE_TYPE d;
+      if (GET_CODE (op) == CONST_INT)
+       lv = INTVAL (op), hv = INTVAL (op) < 0 ? -1 : 0;
+      else
+       lv = CONST_DOUBLE_LOW (op),  hv = CONST_DOUBLE_HIGH (op);
 
 #ifdef REAL_ARITHMETIC
-      REAL_VALUE_FROM_INT (d, CONST_DOUBLE_LOW (op), CONST_DOUBLE_HIGH (op));
+      REAL_VALUE_FROM_INT (d, lv, hv, mode);
 #else
-      if (CONST_DOUBLE_HIGH (op) < 0)
+      if (hv < 0)
        {
-         d = (double) (~ CONST_DOUBLE_HIGH (op));
+         d = (double) (~ hv);
          d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
                * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
-         d += (double) (unsigned HOST_WIDE_INT) (~ CONST_DOUBLE_LOW (op));
+         d += (double) (unsigned HOST_WIDE_INT) (~ lv);
          d = (- d - 1.0);
        }
       else
        {
-         d = (double) CONST_DOUBLE_HIGH (op);
+         d = (double) hv;
          d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
                * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
-         d += (double) (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op);
+         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_CODE (op) == CONST_DOUBLE
-          && GET_MODE (op) == VOIDmode)
+  else if (code == UNSIGNED_FLOAT && GET_MODE (op) == VOIDmode
+          && (GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT))
     {
+      HOST_WIDE_INT hv, lv;
       REAL_VALUE_TYPE d;
 
+      if (GET_CODE (op) == CONST_INT)
+       lv = INTVAL (op), hv = INTVAL (op) < 0 ? -1 : 0;
+      else
+       lv = CONST_DOUBLE_LOW (op),  hv = CONST_DOUBLE_HIGH (op);
+
+      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, CONST_DOUBLE_LOW (op),
-                                   CONST_DOUBLE_HIGH (op));
+      REAL_VALUE_FROM_UNSIGNED_INT (d, lv, hv, mode);
 #else
-      d = (double) CONST_DOUBLE_HIGH (op);
+
+      d = (double) (unsigned HOST_WIDE_INT) hv;
       d *= ((double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2))
            * (double) ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT / 2)));
-      d += (double) (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op);
+      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
@@ -3020,14 +3188,14 @@ 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. */
-  else if (GET_MODE (op) == VOIDmode
+     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))
     {
       HOST_WIDE_INT l1, h1, lv, hv;
@@ -3064,10 +3232,8 @@ simplify_unary_operation (code, mode, op, op_mode)
          break;
 
        case TRUNCATE:
-         if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
-           return GEN_INT (l1 & GET_MODE_MASK (mode));
-         else
-           return 0;
+         /* This is just a change-of-mode, so do nothing.  */
+         lv = l1, hv = h1;
          break;
 
        case ZERO_EXTEND:
@@ -3157,11 +3323,14 @@ simplify_unary_operation (code, mode, op, op_mode)
          abort ();
        }
 
-      x = immed_real_const_1 (d, mode);
+      x = CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
       set_float_handler (NULL_PTR);
       return x;
     }
-  else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE_CLASS (mode) == MODE_INT
+
+  else if (GET_CODE (op) == CONST_DOUBLE
+          && GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
+          && GET_MODE_CLASS (mode) == MODE_INT
           && width <= HOST_BITS_PER_WIDE_INT && width > 0)
     {
       REAL_VALUE_TYPE d;
@@ -3200,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
@@ -3229,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;
@@ -3311,20 +3509,24 @@ simplify_binary_operation (code, mode, op0, op1)
        }
 #endif
 
-      set_float_handler (NULL_PTR);
       value = real_value_truncate (mode, value);
-      return immed_real_const_1 (value, mode);
+      set_float_handler (NULL_PTR);
+      return CONST_DOUBLE_FROM_REAL_VALUE (value, mode);
     }
 #endif  /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
 
   /* We can fold some multi-word operations.  */
   if (GET_MODE_CLASS (mode) == MODE_INT
-      && GET_CODE (op0) == CONST_DOUBLE
+      && width == HOST_BITS_PER_WIDE_INT * 2
+      && (GET_CODE (op0) == CONST_DOUBLE || GET_CODE (op0) == CONST_INT)
       && (GET_CODE (op1) == CONST_DOUBLE || GET_CODE (op1) == CONST_INT))
     {
       HOST_WIDE_INT l1, l2, h1, h2, lv, hv;
 
-      l1 = CONST_DOUBLE_LOW (op0), h1 = CONST_DOUBLE_HIGH (op0);
+      if (GET_CODE (op0) == CONST_DOUBLE)
+       l1 = CONST_DOUBLE_LOW (op0), h1 = CONST_DOUBLE_HIGH (op0);
+      else
+       l1 = INTVAL (op0), h1 = l1 < 0 ? -1 : 0;
 
       if (GET_CODE (op1) == CONST_DOUBLE)
        l2 = CONST_DOUBLE_LOW (op1), h2 = CONST_DOUBLE_HIGH (op1);
@@ -3338,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);
@@ -3406,7 +3608,7 @@ simplify_binary_operation (code, mode, op0, op1)
          break;
 
        case LSHIFTRT:   case ASHIFTRT:
-       case ASHIFT:     case LSHIFT:
+       case ASHIFT:
        case ROTATE:     case ROTATERT:
 #ifdef SHIFT_COUNT_TRUNCATED
          if (SHIFT_COUNT_TRUNCATED)
@@ -3419,9 +3621,8 @@ simplify_binary_operation (code, mode, op0, op1)
          if (code == LSHIFTRT || code == ASHIFTRT)
            rshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv,
                           code == ASHIFTRT);
-         else if (code == ASHIFT || code == LSHIFT)
-           lshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv,
-                          code == ASHIFT);
+         else if (code == ASHIFT)
+           lshift_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv, 1);
          else if (code == ROTATE)
            lrotate_double (l1, h1, l2, GET_MODE_BITSIZE (mode), &lv, &hv);
          else /* code == ROTATERT */
@@ -3447,7 +3648,7 @@ simplify_binary_operation (code, mode, op0, op1)
          /* In IEEE floating point, x+0 is not the same as x.  Similarly
             for the other optimizations below.  */
          if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
-             && FLOAT_MODE_P (mode))
+             && FLOAT_MODE_P (mode) && ! flag_fast_math)
            break;
 
          if (op1 == CONST0_RTX (mode))
@@ -3461,13 +3662,71 @@ simplify_binary_operation (code, mode, op0, op1)
 
          /* Handle both-operands-constant cases.  We can only add
             CONST_INTs to constants since the sum of relocatable symbols
-            can't be handled by most assemblers.  */
+            can't be handled by most assemblers.  Don't add CONST_INT
+            to CONST_INT since overflow won't be computed properly if wider
+            than HOST_BITS_PER_WIDE_INT.  */
 
-         if (CONSTANT_P (op0) && GET_CODE (op1) == CONST_INT)
+         if (CONSTANT_P (op0) && GET_MODE (op0) != VOIDmode
+             && GET_CODE (op1) == CONST_INT)
            return plus_constant (op0, INTVAL (op1));
-         else if (CONSTANT_P (op1) && GET_CODE (op0) == CONST_INT)
+         else if (CONSTANT_P (op1) && GET_MODE (op1) != VOIDmode
+                  && GET_CODE (op0) == CONST_INT)
            return plus_constant (op1, INTVAL (op0));
 
+         /* See if this is something like X * C - X or vice versa or
+            if the multiplication is written as a shift.  If so, we can
+            distribute and make a new multiply, shift, or maybe just
+            have X (if C is 2 in the example above).  But don't make
+            real multiply if we didn't have one before.  */
+
+         if (! FLOAT_MODE_P (mode))
+           {
+             HOST_WIDE_INT coeff0 = 1, coeff1 = 1;
+             rtx lhs = op0, rhs = op1;
+             int had_mult = 0;
+
+             if (GET_CODE (lhs) == NEG)
+               coeff0 = -1, lhs = XEXP (lhs, 0);
+             else if (GET_CODE (lhs) == MULT
+                      && GET_CODE (XEXP (lhs, 1)) == CONST_INT)
+               {
+                 coeff0 = INTVAL (XEXP (lhs, 1)), lhs = XEXP (lhs, 0);
+                 had_mult = 1;
+               }
+             else if (GET_CODE (lhs) == ASHIFT
+                      && GET_CODE (XEXP (lhs, 1)) == CONST_INT
+                      && INTVAL (XEXP (lhs, 1)) >= 0
+                      && INTVAL (XEXP (lhs, 1)) < HOST_BITS_PER_WIDE_INT)
+               {
+                 coeff0 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (lhs, 1));
+                 lhs = XEXP (lhs, 0);
+               }
+
+             if (GET_CODE (rhs) == NEG)
+               coeff1 = -1, rhs = XEXP (rhs, 0);
+             else if (GET_CODE (rhs) == MULT
+                      && GET_CODE (XEXP (rhs, 1)) == CONST_INT)
+               {
+                 coeff1 = INTVAL (XEXP (rhs, 1)), rhs = XEXP (rhs, 0);
+                 had_mult = 1;
+               }
+             else if (GET_CODE (rhs) == ASHIFT
+                      && GET_CODE (XEXP (rhs, 1)) == CONST_INT
+                      && INTVAL (XEXP (rhs, 1)) >= 0
+                      && INTVAL (XEXP (rhs, 1)) < HOST_BITS_PER_WIDE_INT)
+               {
+                 coeff1 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (rhs, 1));
+                 rhs = XEXP (rhs, 0);
+               }
+
+             if (rtx_equal_p (lhs, rhs))
+               {
+                 tem = cse_gen_binary (MULT, mode, lhs,
+                                       GEN_INT (coeff0 + coeff1));
+                 return (GET_CODE (tem) == MULT && ! had_mult) ? 0 : tem;
+               }
+           }
+
          /* If one of the operands is a PLUS or a MINUS, see if we can
             simplify this by the associative law. 
             Don't use the associative law for floating point.
@@ -3490,7 +3749,7 @@ simplify_binary_operation (code, mode, op0, op1)
             In IEEE floating point, x-0 is not the same as x.  */
 
          if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
-              || ! FLOAT_MODE_P (mode))
+              || ! FLOAT_MODE_P (mode) || flag_fast_math)
              && op1 == CONST0_RTX (mode))
            return op0;
 #else
@@ -3502,14 +3761,16 @@ simplify_binary_operation (code, mode, op0, op1)
          /* None of these optimizations can be done for IEEE
             floating point.  */
          if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
-             && FLOAT_MODE_P (mode))
+             && FLOAT_MODE_P (mode) && ! flag_fast_math)
            break;
 
-         /* We can't assume x-x is 0 even with non-IEEE floating point.  */
+         /* We can't assume x-x is 0 even with non-IEEE floating point,
+            but since it is zero except in very strange circumstances, we
+            will treat it as zero with -ffast-math.  */
          if (rtx_equal_p (op0, op1)
              && ! side_effects_p (op0)
-             && ! FLOAT_MODE_P (mode))
-           return const0_rtx;
+             && (! FLOAT_MODE_P (mode) || flag_fast_math))
+           return CONST0_RTX (mode);
 
          /* Change subtraction from zero into negation.  */
          if (op0 == CONST0_RTX (mode))
@@ -3523,6 +3784,60 @@ simplify_binary_operation (code, mode, op0, op1)
          if (op1 == CONST0_RTX (mode))
            return op0;
 
+         /* See if this is something like X * C - X or vice versa or
+            if the multiplication is written as a shift.  If so, we can
+            distribute and make a new multiply, shift, or maybe just
+            have X (if C is 2 in the example above).  But don't make
+            real multiply if we didn't have one before.  */
+
+         if (! FLOAT_MODE_P (mode))
+           {
+             HOST_WIDE_INT coeff0 = 1, coeff1 = 1;
+             rtx lhs = op0, rhs = op1;
+             int had_mult = 0;
+
+             if (GET_CODE (lhs) == NEG)
+               coeff0 = -1, lhs = XEXP (lhs, 0);
+             else if (GET_CODE (lhs) == MULT
+                      && GET_CODE (XEXP (lhs, 1)) == CONST_INT)
+               {
+                 coeff0 = INTVAL (XEXP (lhs, 1)), lhs = XEXP (lhs, 0);
+                 had_mult = 1;
+               }
+             else if (GET_CODE (lhs) == ASHIFT
+                      && GET_CODE (XEXP (lhs, 1)) == CONST_INT
+                      && INTVAL (XEXP (lhs, 1)) >= 0
+                      && INTVAL (XEXP (lhs, 1)) < HOST_BITS_PER_WIDE_INT)
+               {
+                 coeff0 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (lhs, 1));
+                 lhs = XEXP (lhs, 0);
+               }
+
+             if (GET_CODE (rhs) == NEG)
+               coeff1 = - 1, rhs = XEXP (rhs, 0);
+             else if (GET_CODE (rhs) == MULT
+                      && GET_CODE (XEXP (rhs, 1)) == CONST_INT)
+               {
+                 coeff1 = INTVAL (XEXP (rhs, 1)), rhs = XEXP (rhs, 0);
+                 had_mult = 1;
+               }
+             else if (GET_CODE (rhs) == ASHIFT
+                      && GET_CODE (XEXP (rhs, 1)) == CONST_INT
+                      && INTVAL (XEXP (rhs, 1)) >= 0
+                      && INTVAL (XEXP (rhs, 1)) < HOST_BITS_PER_WIDE_INT)
+               {
+                 coeff1 = ((HOST_WIDE_INT) 1) << INTVAL (XEXP (rhs, 1));
+                 rhs = XEXP (rhs, 0);
+               }
+
+             if (rtx_equal_p (lhs, rhs))
+               {
+                 tem = cse_gen_binary (MULT, mode, lhs,
+                                       GEN_INT (coeff0 - coeff1));
+                 return (GET_CODE (tem) == MULT && ! had_mult) ? 0 : tem;
+               }
+           }
+
          /* (a - (-b)) -> (a + b).  */
          if (GET_CODE (op1) == NEG)
            return cse_gen_binary (PLUS, mode, op0, XEXP (op1, 0));
@@ -3540,8 +3855,17 @@ simplify_binary_operation (code, mode, op0, op1)
            return tem;
 
          /* Don't let a relocatable value get a negative coeff.  */
-         if (GET_CODE (op1) == CONST_INT)
+         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:
@@ -3554,7 +3878,7 @@ simplify_binary_operation (code, mode, op0, op1)
 
          /* In IEEE floating point, x*0 is not always 0.  */
          if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
-              && ! FLOAT_MODE_P (mode))
+              || ! FLOAT_MODE_P (mode) || flag_fast_math)
              && op1 == CONST0_RTX (mode)
              && ! side_effects_p (op0))
            return op1;
@@ -3565,9 +3889,16 @@ simplify_binary_operation (code, mode, op0, op1)
          if (op1 == CONST1_RTX (mode))
            return op0;
 
-         /* Convert multiply by constant power of two into shift.  */
+         /* Convert multiply by constant power of two into shift unless
+            we are still generating RTL.  This test is a kludge.  */
          if (GET_CODE (op1) == CONST_INT
-             && (val = exact_log2 (INTVAL (op1))) >= 0)
+             && (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));
 
          if (GET_CODE (op1) == CONST_DOUBLE
@@ -3646,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))
@@ -3654,32 +3985,35 @@ simplify_binary_operation (code, mode, op0, op1)
 
          /* In IEEE floating point, 0/x is not always 0.  */
          if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
-              || ! FLOAT_MODE_P (mode))
+              || ! FLOAT_MODE_P (mode) || flag_fast_math)
              && op0 == CONST0_RTX (mode)
              && ! side_effects_p (op1))
            return op0;
 
-#if 0 /* Turned off till an expert says this is a safe thing to do.  */
 #if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
-         /* Change division by a constant into multiplication.  */
+         /* Change division by a constant into multiplication.  Only do
+            this with -ffast-math until an expert says it is safe in
+            general.  */
          else if (GET_CODE (op1) == CONST_DOUBLE
                   && GET_MODE_CLASS (GET_MODE (op1)) == MODE_FLOAT
-                  && op1 != CONST0_RTX (mode))
+                  && op1 != CONST0_RTX (mode)
+                  && flag_fast_math)
            {
              REAL_VALUE_TYPE d;
              REAL_VALUE_FROM_CONST_DOUBLE (d, op1);
-             if (REAL_VALUES_EQUAL (d, dconst0))
-               abort();
+
+             if (! REAL_VALUES_EQUAL (d, dconst0))
+               {
 #if defined (REAL_ARITHMETIC)
-             REAL_ARITHMETIC (d, (int) RDIV_EXPR, dconst1, d);
-             return gen_rtx (MULT, mode, op0, 
-                             CONST_DOUBLE_FROM_REAL_VALUE (d, mode));
+                 REAL_ARITHMETIC (d, rtx_to_tree_code (DIV), dconst1, d);
+                 return gen_rtx (MULT, mode, op0, 
+                                 CONST_DOUBLE_FROM_REAL_VALUE (d, mode));
 #else
-             return gen_rtx (MULT, mode, op0, 
-                             CONST_DOUBLE_FROM_REAL_VALUE (1./d, mode));
-           }
-#endif
+                 return gen_rtx (MULT, mode, op0, 
+                                 CONST_DOUBLE_FROM_REAL_VALUE (1./d, mode));
 #endif
+               }
+           }
 #endif
          break;
 
@@ -3689,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)
@@ -3705,9 +4039,8 @@ simplify_binary_operation (code, mode, op0, op1)
              && ! side_effects_p (op1))
            return op0;
 
-         /* ... fall through ... */
+         /* ... fall through ...  */
 
-       case LSHIFT:
        case ASHIFT:
        case ASHIFTRT:
        case LSHIFTRT:
@@ -3842,28 +4175,21 @@ simplify_binary_operation (code, mode, op0, op1)
 
 #ifdef SHIFT_COUNT_TRUNCATED
       if (SHIFT_COUNT_TRUNCATED)
-       arg1 &= (BITS_PER_WORD - 1);
+       arg1 %= width;
 #endif
 
-      if (arg1 >= width)
-       return 0;
-
       val = ((unsigned HOST_WIDE_INT) arg0) >> arg1;
       break;
 
     case ASHIFT:
-    case LSHIFT:
       if (arg1 < 0)
        return 0;
 
 #ifdef SHIFT_COUNT_TRUNCATED
       if (SHIFT_COUNT_TRUNCATED)
-       arg1 &= (BITS_PER_WORD - 1);
+       arg1 %= width;
 #endif
 
-      if (arg1 >= width)
-       return 0;
-
       val = ((unsigned HOST_WIDE_INT) arg0) << arg1;
       break;
 
@@ -3873,12 +4199,9 @@ simplify_binary_operation (code, mode, op0, op1)
 
 #ifdef SHIFT_COUNT_TRUNCATED
       if (SHIFT_COUNT_TRUNCATED)
-       arg1 &= (BITS_PER_WORD - 1);
+       arg1 %= width;
 #endif
 
-      if (arg1 >= width)
-       return 0;
-
       val = arg0s >> arg1;
 
       /* Bootstrap compiler may not have sign extended the right shift.
@@ -3940,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
@@ -3963,7 +4299,7 @@ simplify_plus_minus (code, mode, op0, op1)
   int first = 1, negate = 0, changed;
   int i, j;
 
-  bzero (ops, sizeof ops);
+  bzero ((char *) ops, sizeof ops);
   
   /* Set up the two operands and then expand them until nothing has been
      changed.  If we run out of room in our array, give up; this should
@@ -4158,7 +4494,12 @@ cse_gen_binary (code, mode, op0, op1)
 }
 \f
 /* Like simplify_binary_operation except used for relational operators.
-   MODE is the mode of the operands, not that of the result.  */
+   MODE is the mode of the operands, not that of the result.  If MODE
+   is VOIDmode, both operands must also be VOIDmode and we compare the
+   operands in "infinite precision".
+
+   If no simplification is possible, this function returns zero.  Otherwise,
+   it returns either const_true_rtx or const0_rtx.  */
 
 rtx
 simplify_relational_operation (code, mode, op0, op1)
@@ -4166,164 +4507,161 @@ simplify_relational_operation (code, mode, op0, op1)
      enum machine_mode mode;
      rtx op0, op1;
 {
-  register HOST_WIDE_INT arg0, arg1, arg0s, arg1s;
-  HOST_WIDE_INT val;
-  int width = GET_MODE_BITSIZE (mode);
+  int equal, op0lt, op0ltu, op1lt, op1ltu;
+  rtx tem;
 
   /* If op0 is a compare, extract the comparison arguments from it.  */
   if (GET_CODE (op0) == COMPARE && op1 == const0_rtx)
     op1 = XEXP (op0, 1), op0 = XEXP (op0, 0);
 
-  /* What to do with MODE_CC isn't clear yet.
-     Let's make sure nothing erroneous is done.  */
-  if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC)
+  /* We can't simplify MODE_CC values since we don't know what the
+     actual comparison is.  */
+  if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC
+#ifdef HAVE_cc0
+      || op0 == cc0_rtx
+#endif
+      )
     return 0;
 
-  /* Unlike the arithmetic operations, we can do the comparison whether
-     or not WIDTH is larger than HOST_BITS_PER_WIDE_INT because the
-     CONST_INTs are to be understood as being infinite precision as
-     is the comparison.  So there is no question of overflow.  */
-
-  if (GET_CODE (op0) != CONST_INT || GET_CODE (op1) != CONST_INT || width == 0)
-    {
-      /* Even if we can't compute a constant result,
-        there are some cases worth simplifying.  */
-
-      /* For non-IEEE floating-point, if the two operands are equal, we know
-        the result.  */
-      if (rtx_equal_p (op0, op1)
-         && (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
-             || ! FLOAT_MODE_P (GET_MODE (op0))))
-       return (code == EQ || code == GE || code == LE || code == LEU
-               || code == GEU) ? const_true_rtx : const0_rtx;
-
+  /* For integer comparisons of A and B maybe we can simplify A - B and can
+     then simplify a comparison of that with zero.  If A and B are both either
+     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, 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))
+      && code != GTU && code != GEU && code != LTU && code != LEU)
+    return simplify_relational_operation (signed_condition (code),
+                                         mode, tem, const0_rtx);
+
+  /* For non-IEEE floating-point, if the two operands are equal, we know the
+     result.  */
+  if (rtx_equal_p (op0, op1)
+      && (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+         || ! FLOAT_MODE_P (GET_MODE (op0)) || flag_fast_math))
+    equal = 1, op0lt = 0, op0ltu = 0, op1lt = 0, op1ltu = 0;
+
+  /* If the operands are floating-point constants, see if we can fold
+     the result.  */
 #if ! defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC)
-      else if (GET_CODE (op0) == CONST_DOUBLE
-              && GET_CODE (op1) == CONST_DOUBLE
-              && GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
-       {
-         REAL_VALUE_TYPE d0, d1;
-         jmp_buf handler;
-         int op0lt, op1lt, equal;
+  else if (GET_CODE (op0) == CONST_DOUBLE && GET_CODE (op1) == CONST_DOUBLE
+          && GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
+    {
+      REAL_VALUE_TYPE d0, d1;
+      jmp_buf handler;
+      
+      if (setjmp (handler))
+       return 0;
 
-         if (setjmp (handler))
-           return 0;
+      set_float_handler (handler);
+      REAL_VALUE_FROM_CONST_DOUBLE (d0, op0);
+      REAL_VALUE_FROM_CONST_DOUBLE (d1, op1);
+      equal = REAL_VALUES_EQUAL (d0, d1);
+      op0lt = op0ltu = REAL_VALUES_LESS (d0, d1);
+      op1lt = op1ltu = REAL_VALUES_LESS (d1, d0);
+      set_float_handler (NULL_PTR);
+    }
+#endif  /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
 
-         set_float_handler (handler);
-         REAL_VALUE_FROM_CONST_DOUBLE (d0, op0);
-         REAL_VALUE_FROM_CONST_DOUBLE (d1, op1);
-         equal = REAL_VALUES_EQUAL (d0, d1);
-         op0lt = REAL_VALUES_LESS (d0, d1);
-         op1lt = REAL_VALUES_LESS (d1, d0);
-         set_float_handler (NULL_PTR);
+  /* Otherwise, see if the operands are both integers.  */
+  else if ((GET_MODE_CLASS (mode) == MODE_INT || mode == VOIDmode)
+          && (GET_CODE (op0) == CONST_DOUBLE || GET_CODE (op0) == CONST_INT)
+          && (GET_CODE (op1) == CONST_DOUBLE || GET_CODE (op1) == CONST_INT))
+    {
+      int width = GET_MODE_BITSIZE (mode);
+      HOST_WIDE_INT l0s, h0s, l1s, h1s;
+      unsigned HOST_WIDE_INT l0u, h0u, l1u, h1u;
 
-         switch (code)
-           {
-           case EQ:
-             return equal ? const_true_rtx : const0_rtx;
-           case NE:
-             return !equal ? const_true_rtx : const0_rtx;
-           case LE:
-             return equal || op0lt ? const_true_rtx : const0_rtx;
-           case LT:
-             return op0lt ? const_true_rtx : const0_rtx;
-           case GE:
-             return equal || op1lt ? const_true_rtx : const0_rtx;
-           case GT:
-             return op1lt ? const_true_rtx : const0_rtx;
-           }
+      /* Get the two words comprising each integer constant.  */
+      if (GET_CODE (op0) == CONST_DOUBLE)
+       {
+         l0u = l0s = CONST_DOUBLE_LOW (op0);
+         h0u = h0s = CONST_DOUBLE_HIGH (op0);
        }
-#endif  /* not REAL_IS_NOT_DOUBLE, or REAL_ARITHMETIC */
-
-      else if (GET_MODE_CLASS (mode) == MODE_INT
-              && width > HOST_BITS_PER_WIDE_INT
-              && (GET_CODE (op0) == CONST_DOUBLE
-                  || GET_CODE (op0) == CONST_INT)
-              && (GET_CODE (op1) == CONST_DOUBLE
-                  || GET_CODE (op1) == CONST_INT))
+      else
        {
-         HOST_WIDE_INT h0, l0, h1, l1;
-         unsigned HOST_WIDE_INT uh0, ul0, uh1, ul1;
-         int op0lt, op0ltu, equal;
-
-         if (GET_CODE (op0) == CONST_DOUBLE)
-           l0 = CONST_DOUBLE_LOW (op0), h0 = CONST_DOUBLE_HIGH (op0);
-         else
-           l0 = INTVAL (op0), h0 = l0 < 0 ? -1 : 0;
+         l0u = l0s = INTVAL (op0);
+         h0u = 0, h0s = l0s < 0 ? -1 : 0;
+       }
          
-         if (GET_CODE (op1) == CONST_DOUBLE)
-           l1 = CONST_DOUBLE_LOW (op1), h1 = CONST_DOUBLE_HIGH (op1);
-         else
-           l1 = INTVAL (op1), h1 = l1 < 0 ? -1 : 0;
+      if (GET_CODE (op1) == CONST_DOUBLE)
+       {
+         l1u = l1s = CONST_DOUBLE_LOW (op1);
+         h1u = h1s = CONST_DOUBLE_HIGH (op1);
+       }
+      else
+       {
+         l1u = l1s = INTVAL (op1);
+         h1u = 0, h1s = l1s < 0 ? -1 : 0;
+       }
+
+      /* If WIDTH is nonzero and smaller than HOST_BITS_PER_WIDE_INT,
+        we have to sign or zero-extend the values.  */
+      if (width != 0 && width <= HOST_BITS_PER_WIDE_INT)
+       h0u = h1u = 0, h0s = l0s < 0 ? -1 : 0, h1s = l1s < 0 ? -1 : 0;
 
-         uh0 = h0, ul0 = l0, uh1 = h1, ul1 = l1;
+      if (width != 0 && width < HOST_BITS_PER_WIDE_INT)
+       {
+         l0u &= ((HOST_WIDE_INT) 1 << width) - 1;
+         l1u &= ((HOST_WIDE_INT) 1 << width) - 1;
 
-         equal = (h0 == h1 && l0 == l1);
-         op0lt = (h0 < h1 || (h0 == h1 && l0 < l1));
-         op0ltu = (uh0 < uh1 || (uh0 == uh1 && ul0 < ul1));
+         if (l0s & ((HOST_WIDE_INT) 1 << (width - 1)))
+           l0s |= ((HOST_WIDE_INT) (-1) << width);
 
-         switch (code)
-           {
-           case EQ:
-             return equal ? const_true_rtx : const0_rtx;
-           case NE:
-             return !equal ? const_true_rtx : const0_rtx;
-           case LE:
-             return equal || op0lt ? const_true_rtx : const0_rtx;
-           case LT:
-             return op0lt ? const_true_rtx : const0_rtx;
-           case GE:
-             return !op0lt ? const_true_rtx : const0_rtx;
-           case GT:
-             return !equal && !op0lt ? const_true_rtx : const0_rtx;
-           case LEU:
-             return equal || op0ltu ? const_true_rtx : const0_rtx;
-           case LTU:
-             return op0ltu ? const_true_rtx : const0_rtx;
-           case GEU:
-             return !op0ltu ? const_true_rtx : const0_rtx;
-           case GTU:
-             return !equal && !op0ltu ? const_true_rtx : const0_rtx;
-           }
+         if (l1s & ((HOST_WIDE_INT) 1 << (width - 1)))
+           l1s |= ((HOST_WIDE_INT) (-1) << width);
        }
 
+      equal = (h0u == h1u && l0u == l1u);
+      op0lt = (h0s < h1s || (h0s == h1s && l0s < l1s));
+      op1lt = (h1s < h0s || (h1s == h0s && l1s < l0s));
+      op0ltu = (h0u < h1u || (h0u == h1u && l0u < l1u));
+      op1ltu = (h1u < h0u || (h1u == h0u && l1u < l0u));
+    }
+
+  /* Otherwise, there are some code-specific tests we can make.  */
+  else
+    {
       switch (code)
        {
        case EQ:
-         {
-#if 0
-           /* We can't make this assumption due to #pragma weak */
-           if (CONSTANT_P (op0) && op1 == const0_rtx)
-             return const0_rtx;
+         /* References to the frame plus a constant or labels cannot
+            be zero, but a SYMBOL_REF can due to #pragma weak.  */
+         if (((NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx)
+              || GET_CODE (op0) == LABEL_REF)
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+             /* On some machines, the ap reg can be 0 sometimes.  */
+             && op0 != arg_pointer_rtx
 #endif
-           if (NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx
-               /* On some machines, the ap reg can be 0 sometimes.  */
-               && op0 != arg_pointer_rtx)
-             return const0_rtx;
-           break;
-         }
+               )
+           return const0_rtx;
+         break;
 
        case NE:
-#if 0
-         /* We can't make this assumption due to #pragma weak */
-         if (CONSTANT_P (op0) && op1 == const0_rtx)
-           return const_true_rtx;
+         if (((NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx)
+              || GET_CODE (op0) == LABEL_REF)
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+             && op0 != arg_pointer_rtx
 #endif
-         if (NONZERO_BASE_PLUS_P (op0) && op1 == const0_rtx
-             /* On some machines, the ap reg can be 0 sometimes.  */
-             && op0 != arg_pointer_rtx)
+             )
            return const_true_rtx;
          break;
 
        case GEU:
-         /* Unsigned values are never negative, but we must be sure we are
-            actually comparing a value, not a CC operand.  */
-         if (op1 == const0_rtx && INTEGRAL_MODE_P (mode))
+         /* Unsigned values are never negative.  */
+         if (op1 == const0_rtx)
            return const_true_rtx;
          break;
 
        case LTU:
-         if (op1 == const0_rtx && INTEGRAL_MODE_P (mode))
+         if (op1 == const0_rtx)
            return const0_rtx;
          break;
 
@@ -4332,8 +4670,8 @@ simplify_relational_operation (code, mode, op0, op1)
             unsigned value.  */
          if (GET_CODE (op1) == CONST_INT
              && INTVAL (op1) == GET_MODE_MASK (mode)
-             && INTEGRAL_MODE_P (mode))
-           return const_true_rtx;
+           && INTEGRAL_MODE_P (mode))
+         return const_true_rtx;
          break;
 
        case GTU:
@@ -4347,92 +4685,33 @@ simplify_relational_operation (code, mode, op0, op1)
       return 0;
     }
 
-  /* Get the integer argument values in two forms:
-     zero-extended in ARG0, ARG1 and sign-extended in ARG0S, ARG1S.  */
-
-  arg0 = INTVAL (op0);
-  arg1 = INTVAL (op1);
-
-  if (width < HOST_BITS_PER_WIDE_INT)
-    {
-      arg0 &= ((HOST_WIDE_INT) 1 << width) - 1;
-      arg1 &= ((HOST_WIDE_INT) 1 << width) - 1;
-
-      arg0s = arg0;
-      if (arg0s & ((HOST_WIDE_INT) 1 << (width - 1)))
-       arg0s |= ((HOST_WIDE_INT) (-1) << width);
-
-      arg1s = arg1;
-      if (arg1s & ((HOST_WIDE_INT) 1 << (width - 1)))
-       arg1s |= ((HOST_WIDE_INT) (-1) << width);
-    }
-  else
-    {
-      arg0s = arg0;
-      arg1s = arg1;
-    }
-
-  /* Compute the value of the arithmetic.  */
-
+  /* If we reach here, EQUAL, OP0LT, OP0LTU, OP1LT, and OP1LTU are set
+     as appropriate.  */
   switch (code)
     {
-    case NE:
-      val = arg0 != arg1 ? STORE_FLAG_VALUE : 0;
-      break;
-
     case EQ:
-      val = arg0 == arg1 ? STORE_FLAG_VALUE : 0;
-      break;
-
-    case LE:
-      val = arg0s <= arg1s ? STORE_FLAG_VALUE : 0;
-      break;
-
+      return equal ? const_true_rtx : const0_rtx;
+    case NE:
+      return ! equal ? const_true_rtx : const0_rtx;
     case LT:
-      val = arg0s < arg1s ? STORE_FLAG_VALUE : 0;
-      break;
-
-    case GE:
-      val = arg0s >= arg1s ? STORE_FLAG_VALUE : 0;
-      break;
-
+      return op0lt ? const_true_rtx : const0_rtx;
     case GT:
-      val = arg0s > arg1s ? STORE_FLAG_VALUE : 0;
-      break;
-
-    case LEU:
-      val = (((unsigned HOST_WIDE_INT) arg0)
-            <= ((unsigned HOST_WIDE_INT) arg1) ? STORE_FLAG_VALUE : 0);
-      break;
-
+      return op1lt ? const_true_rtx : const0_rtx;
     case LTU:
-      val = (((unsigned HOST_WIDE_INT) arg0)
-            < ((unsigned HOST_WIDE_INT) arg1) ? STORE_FLAG_VALUE : 0);
-      break;
-
-    case GEU:
-      val = (((unsigned HOST_WIDE_INT) arg0)
-            >= ((unsigned HOST_WIDE_INT) arg1) ? STORE_FLAG_VALUE : 0);
-      break;
-
+      return op0ltu ? const_true_rtx : const0_rtx;
     case GTU:
-      val = (((unsigned HOST_WIDE_INT) arg0)
-            > ((unsigned HOST_WIDE_INT) arg1) ? STORE_FLAG_VALUE : 0);
-      break;
-
-    default:
-      abort ();
+      return op1ltu ? const_true_rtx : const0_rtx;
+    case LE:
+      return equal || op0lt ? const_true_rtx : const0_rtx;
+    case GE:
+      return equal || op1lt ? const_true_rtx : const0_rtx;
+    case LEU:
+      return equal || op0ltu ? const_true_rtx : const0_rtx;
+    case GEU:
+      return equal || op1ltu ? const_true_rtx : const0_rtx;
     }
 
-  /* Clear the bits that don't belong in our mode, unless they and our sign
-     bit are all one.  So we get either a reasonable negative value or a
-     reasonable unsigned value for this mode.  */
-  if (width < HOST_BITS_PER_WIDE_INT
-      && ((val & ((HOST_WIDE_INT) (-1) << (width - 1)))
-         != ((HOST_WIDE_INT) (-1) << (width - 1))))
-    val &= ((HOST_WIDE_INT) 1 << width) - 1;
-  
-  return GEN_INT (val);
+  abort ();
 }
 \f
 /* Simplify CODE, an operation with result mode MODE and three operands,
@@ -4464,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.  */
@@ -4729,7 +5009,28 @@ fold_rtx (x, insn)
                  if (op1)
                    op1 = equiv_constant (op1);
 
-                 if (op0 && op1)
+                 /* If we are looking for the low SImode part of 
+                    (ashift:DI c (const_int 32)), it doesn't work
+                    to compute that in SImode, because a 32-bit shift
+                    in SImode is unpredictable.  We know the value is 0.  */
+                 if (op0 && op1
+                     && GET_CODE (elt->exp) == ASHIFT
+                     && GET_CODE (op1) == CONST_INT
+                     && INTVAL (op1) >= GET_MODE_BITSIZE (mode))
+                   {
+                     if (INTVAL (op1) < GET_MODE_BITSIZE (GET_MODE (elt->exp)))
+                       
+                       /* If the count fits in the inner mode's width,
+                          but exceeds the outer mode's width,
+                          the value will get truncated to 0
+                          by the subreg.  */
+                       new = const0_rtx;
+                     else
+                       /* If the count exceeds even the inner mode's width,
+                          don't fold this expression.  */
+                       new = 0;
+                   }
+                 else if (op0 && op1)
                    new = simplify_binary_operation (GET_CODE (elt->exp), mode,
                                                     op0, op1);
                }
@@ -4806,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
@@ -4863,13 +5164,23 @@ fold_rtx (x, insn)
                    if (GET_MODE (table) != Pmode)
                      new = gen_rtx (TRUNCATE, GET_MODE (table), new);
 
-                   return new;
+                   /* Indicate this is a constant.  This isn't a 
+                      valid form of CONST, but it will only be used
+                      to fold the next insns and then discarded, so
+                      it should be safe.  */
+                   return gen_rtx (CONST, GET_MODE (new), new);
                  }
              }
          }
 
        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;
@@ -5036,13 +5347,26 @@ fold_rtx (x, insn)
   switch (GET_RTX_CLASS (code))
     {
     case '1':
-      /* We can't simplify extension ops unless we know the original mode.  */
-      if ((code == ZERO_EXTEND || code == SIGN_EXTEND)
-         && mode_arg0 == VOIDmode)
-       break;
-      new = simplify_unary_operation (code, mode,
-                                     const_arg0 ? const_arg0 : folded_arg0,
-                                     mode_arg0);
+      {
+       int is_const = 0;
+
+       /* We can't simplify extension ops unless we know the
+          original mode.  */
+       if ((code == ZERO_EXTEND || code == SIGN_EXTEND)
+           && mode_arg0 == VOIDmode)
+         break;
+
+       /* If we had a CONST, strip it off and put it back later if we
+          fold.  */
+       if (const_arg0 != 0 && GET_CODE (const_arg0) == CONST)
+         is_const = 1, const_arg0 = XEXP (const_arg0, 0);
+
+       new = simplify_unary_operation (code, mode,
+                                       const_arg0 ? const_arg0 : folded_arg0,
+                                       mode_arg0);
+       if (new != 0 && is_const)
+         new = gen_rtx (CONST, mode, new);
+      }
       break;
       
     case '<':
@@ -5060,7 +5384,8 @@ fold_rtx (x, insn)
 #ifdef FLOAT_STORE_FLAG_VALUE
          if (GET_MODE_CLASS (mode) == MODE_FLOAT)
            {
-             true = immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, mode);
+             true = CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE,
+                                                  mode);
              false = CONST0_RTX (mode);
            }
 #endif
@@ -5077,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
@@ -5103,7 +5430,7 @@ fold_rtx (x, insn)
                 since x might be a NaN.  */
 
              if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
-                  || ! FLOAT_MODE_P (mode_arg0))
+                  || ! FLOAT_MODE_P (mode_arg0) || flag_fast_math)
                  && (folded_arg0 == folded_arg1
                      || (GET_CODE (folded_arg0) == REG
                          && GET_CODE (folded_arg1) == REG
@@ -5169,7 +5496,8 @@ fold_rtx (x, insn)
 #ifdef FLOAT_STORE_FLAG_VALUE
              if (GET_MODE_CLASS (mode) == MODE_FLOAT)
                {
-                 true = immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, mode);
+                 true = CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE,
+                                                      mode);
                  false = CONST0_RTX (mode);
                }
 #endif
@@ -5198,7 +5526,7 @@ fold_rtx (x, insn)
 #ifdef FLOAT_STORE_FLAG_VALUE
       if (new != 0 && GET_MODE_CLASS (mode) == MODE_FLOAT)
        new = ((new == const0_rtx) ? CONST0_RTX (mode)
-              : immed_real_const_1 (FLOAT_STORE_FLAG_VALUE, mode));
+              : CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE, mode));
 #endif
       break;
 
@@ -5213,11 +5541,68 @@ fold_rtx (x, insn)
             ADDR_DIFF_VEC table.  */
          if (const_arg1 && GET_CODE (const_arg1) == LABEL_REF)
            {
-             rtx y = lookup_as_function (folded_arg0, MINUS);
+             rtx y
+               = GET_CODE (folded_arg0) == MINUS ? folded_arg0
+                 : lookup_as_function (folded_arg0, MINUS);
 
              if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
                  && XEXP (XEXP (y, 1), 0) == XEXP (const_arg1, 0))
                return XEXP (y, 0);
+
+             /* Now try for a CONST of a MINUS like the above.  */
+             if ((y = (GET_CODE (folded_arg0) == CONST ? folded_arg0
+                       : lookup_as_function (folded_arg0, CONST))) != 0
+                 && GET_CODE (XEXP (y, 0)) == MINUS
+                 && GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF
+                 && XEXP (XEXP (XEXP (y, 0),1), 0) == XEXP (const_arg1, 0))
+               return XEXP (XEXP (y, 0), 0);
+           }
+
+         /* Likewise if the operands are in the other order.  */
+         if (const_arg0 && GET_CODE (const_arg0) == LABEL_REF)
+           {
+             rtx y
+               = GET_CODE (folded_arg1) == MINUS ? folded_arg1
+                 : lookup_as_function (folded_arg1, MINUS);
+
+             if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
+                 && XEXP (XEXP (y, 1), 0) == XEXP (const_arg0, 0))
+               return XEXP (y, 0);
+
+             /* Now try for a CONST of a MINUS like the above.  */
+             if ((y = (GET_CODE (folded_arg1) == CONST ? folded_arg1
+                       : lookup_as_function (folded_arg1, CONST))) != 0
+                 && GET_CODE (XEXP (y, 0)) == MINUS
+                 && GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF
+                 && XEXP (XEXP (XEXP (y, 0),1), 0) == XEXP (const_arg0, 0))
+               return XEXP (XEXP (y, 0), 0);
+           }
+
+         /* 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 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.  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
+               = lookup (new_const, safe_hash (new_const, mode) % NBUCKETS,
+                         mode);
+
+             if (p)
+               for (p = p->first_same_value; p; p = p->next_same_value)
+                 if (GET_CODE (p->exp) == REG)
+                   return cse_gen_binary (MINUS, mode, folded_arg0,
+                                          canon_reg (p->exp, NULL_RTX));
            }
          goto from_plus;
 
@@ -5233,7 +5618,7 @@ fold_rtx (x, insn)
                                 NULL_RTX);
            }
 
-         /* ... fall through ... */
+         /* ... fall through ...  */
 
        from_plus:
        case SMIN:    case SMAX:      case UMIN:    case UMAX:
@@ -5417,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;
@@ -5505,7 +5888,7 @@ record_jump_cond (code, mode, op0, op1, reversed_nonequality)
      rtx op0, op1;
      int reversed_nonequality;
 {
-  int op0_hash_code, op1_hash_code;
+  unsigned op0_hash, op1_hash;
   int op0_in_memory, op0_in_struct, op1_in_memory, op1_in_struct;
   struct table_elt *op0_elt, *op1_elt;
 
@@ -5577,7 +5960,7 @@ record_jump_cond (code, mode, op0, op1, reversed_nonequality)
   do_not_record = 0;
   hash_arg_in_memory = 0;
   hash_arg_in_struct = 0;
-  op0_hash_code = HASH (op0, mode);
+  op0_hash = HASH (op0, mode);
   op0_in_memory = hash_arg_in_memory;
   op0_in_struct = hash_arg_in_struct;
 
@@ -5587,7 +5970,7 @@ record_jump_cond (code, mode, op0, op1, reversed_nonequality)
   do_not_record = 0;
   hash_arg_in_memory = 0;
   hash_arg_in_struct = 0;
-  op1_hash_code = HASH (op1, mode);
+  op1_hash = HASH (op1, mode);
   op1_in_memory = hash_arg_in_memory;
   op1_in_struct = hash_arg_in_struct;
   
@@ -5595,8 +5978,15 @@ record_jump_cond (code, mode, op0, op1, reversed_nonequality)
     return;
 
   /* Look up both operands.  */
-  op0_elt = lookup (op0, op0_hash_code, mode);
-  op1_elt = lookup (op1, op1_hash_code, mode);
+  op0_elt = lookup (op0, op0_hash, mode);
+  op1_elt = lookup (op1, op1_hash, mode);
+
+  /* If both operands are already equivalent or if they are not in the
+     table but are identical, do nothing.  */
+  if ((op0_elt != 0 && op1_elt != 0
+       && op0_elt->first_same_value == op1_elt->first_same_value)
+      || op0 == op1 || rtx_equal_p (op0, op1))
+    return;
 
   /* If we aren't setting two things equal all we can do is save this
      comparison.   Similarly if this is floating-point.  In the latter
@@ -5624,16 +6014,16 @@ record_jump_cond (code, mode, op0, op1, reversed_nonequality)
          if (insert_regs (op0, NULL_PTR, 0))
            {
              rehash_using_reg (op0);
-             op0_hash_code = HASH (op0, mode);
+             op0_hash = HASH (op0, mode);
 
              /* If OP0 is contained in OP1, this changes its hash code
                 as well.  Faster to rehash than to check, except
                 for the simple case of a constant.  */
              if (! CONSTANT_P (op1))
-               op1_hash_code = HASH (op1,mode);
+               op1_hash = HASH (op1,mode);
            }
 
-         op0_elt = insert (op0, NULL_PTR, op0_hash_code, mode);
+         op0_elt = insert (op0, NULL_PTR, op0_hash, mode);
          op0_elt->in_memory = op0_in_memory;
          op0_elt->in_struct = op0_in_struct;
        }
@@ -5642,7 +6032,7 @@ record_jump_cond (code, mode, op0, op1, reversed_nonequality)
       if (GET_CODE (op1) == REG)
        {
          /* Look it up again--in case op0 and op1 are the same.  */
-         op1_elt = lookup (op1, op1_hash_code, mode);
+         op1_elt = lookup (op1, op1_hash, mode);
 
          /* Put OP1 in the hash table so it gets a new quantity number.  */
          if (op1_elt == 0)
@@ -5650,10 +6040,10 @@ record_jump_cond (code, mode, op0, op1, reversed_nonequality)
              if (insert_regs (op1, NULL_PTR, 0))
                {
                  rehash_using_reg (op1);
-                 op1_hash_code = HASH (op1, mode);
+                 op1_hash = HASH (op1, mode);
                }
 
-             op1_elt = insert (op1, NULL_PTR, op1_hash_code, mode);
+             op1_elt = insert (op1, NULL_PTR, op1_hash, mode);
              op1_elt->in_memory = op1_in_memory;
              op1_elt->in_struct = op1_in_struct;
            }
@@ -5678,10 +6068,10 @@ record_jump_cond (code, mode, op0, op1, reversed_nonequality)
       if (insert_regs (op0, NULL_PTR, 0))
        {
          rehash_using_reg (op0);
-         op0_hash_code = HASH (op0, mode);
+         op0_hash = HASH (op0, mode);
        }
 
-      op0_elt = insert (op0, NULL_PTR, op0_hash_code, mode);
+      op0_elt = insert (op0, NULL_PTR, op0_hash, mode);
       op0_elt->in_memory = op0_in_memory;
       op0_elt->in_struct = op0_in_struct;
     }
@@ -5691,10 +6081,10 @@ record_jump_cond (code, mode, op0, op1, reversed_nonequality)
       if (insert_regs (op1, NULL_PTR, 0))
        {
          rehash_using_reg (op1);
-         op1_hash_code = HASH (op1, mode);
+         op1_hash = HASH (op1, mode);
        }
 
-      op1_elt = insert (op1, NULL_PTR, op1_hash_code, mode);
+      op1_elt = insert (op1, NULL_PTR, op1_hash, mode);
       op1_elt->in_memory = op1_in_memory;
       op1_elt->in_struct = op1_in_struct;
     }
@@ -5722,10 +6112,10 @@ struct set
   rtx src;
   /* The hash-table element for the SET_SRC of the SET.  */
   struct table_elt *src_elt;
-  /* Hash code for the SET_SRC.  */
-  int src_hash_code;
-  /* Hash code for the SET_DEST.  */
-  int dest_hash_code;
+  /* Hash value for the SET_SRC.  */
+  unsigned src_hash;
+  /* Hash value for the SET_DEST.  */
+  unsigned dest_hash;
   /* The SET_DEST, with SUBREG, etc., stripped.  */
   rtx inner_dest;
   /* Place where the pointer to the INNER_DEST was found.  */
@@ -5741,8 +6131,8 @@ struct set
   enum machine_mode mode;
   /* A constant equivalent for SET_SRC, if any.  */
   rtx src_const;
-  /* Hash code of constant equivalent for SET_SRC.  */
-  int src_const_hash_code;
+  /* Hash value of constant equivalent for SET_SRC.  */
+  unsigned src_const_hash;
   /* Table entry for constant equivalent for SET_SRC, if any.  */
   struct table_elt *src_const_elt;
 };
@@ -5753,8 +6143,8 @@ cse_insn (insn, in_libcall_block)
      int in_libcall_block;
 {
   register rtx x = PATTERN (insn);
-  rtx tem;
   register int i;
+  rtx tem;
   register int n_sets = 0;
 
   /* Records what this insn does to set CC0.  */
@@ -5768,7 +6158,7 @@ cse_insn (insn, in_libcall_block)
   int src_eqv_volatile;
   int src_eqv_in_memory;
   int src_eqv_in_struct;
-  int src_eqv_hash_code;
+  unsigned src_eqv_hash;
 
   struct set *sets;
 
@@ -5780,6 +6170,13 @@ cse_insn (insn, in_libcall_block)
      Also determine whether there is a CLOBBER that invalidates
      all memory references, or all references at varying addresses.  */
 
+  if (GET_CODE (insn) == CALL_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);
+    }
+
   if (GET_CODE (x) == SET)
     {
       sets = (struct set *) alloca (sizeof (struct set));
@@ -5801,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;
@@ -5833,10 +6230,17 @@ cse_insn (insn, in_libcall_block)
       for (i = 0; i < lim; i++)
        {
          register rtx y = XVECEXP (x, 0, i);
-         if (GET_CODE (y) == CLOBBER
-             && (GET_CODE (XEXP (y, 0)) == REG
-                 || GET_CODE (XEXP (y, 0)) == SUBREG))
-           invalidate (XEXP (y, 0));
+         if (GET_CODE (y) == CLOBBER)
+           {
+             rtx clobbered = XEXP (y, 0);
+
+             if (GET_CODE (clobbered) == REG
+                 || 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));
+           }
        }
            
       for (i = 0; i < lim; i++)
@@ -5851,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)
@@ -5907,14 +6311,15 @@ cse_insn (insn, in_libcall_block)
       fold_rtx (x, insn);
     }
 
-  if (n_sets == 1 && REG_NOTES (insn) != 0)
-    {
-      /* Store the equivalent value in SRC_EQV, if different.  */
-      rtx tem = find_reg_note (insn, REG_EQUAL, NULL_RTX);
-
-      if (tem && ! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl)))
-        src_eqv = canon_reg (XEXP (tem, 0), NULL_RTX);
-    }
+  /* 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 = canon_reg (XEXP (tem, 0), NULL_RTX);
 
   /* Canonicalize sources and addresses of destinations.
      We do this in a separate pass to avoid problems when a MATCH_DUP is
@@ -5931,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;
@@ -6009,12 +6416,12 @@ cse_insn (insn, in_libcall_block)
          hash_arg_in_memory = 0;
          hash_arg_in_struct = 0;
          src_eqv = fold_rtx (src_eqv, insn);
-         src_eqv_hash_code = HASH (src_eqv, eqvmode);
+         src_eqv_hash = HASH (src_eqv, eqvmode);
 
          /* Find the equivalence class for the equivalent expression.  */
 
          if (!do_not_record)
-           src_eqv_elt = lookup (src_eqv, src_eqv_hash_code, eqvmode);
+           src_eqv_elt = lookup (src_eqv, src_eqv_hash, eqvmode);
 
          src_eqv_volatile = do_not_record;
          src_eqv_in_memory = hash_arg_in_memory;
@@ -6023,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
@@ -6033,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
@@ -6048,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,
@@ -6057,7 +6472,7 @@ cse_insn (insn, in_libcall_block)
       hash_arg_in_struct = 0;
 
       sets[i].src = src;
-      sets[i].src_hash_code = HASH (src, mode);
+      sets[i].src_hash = HASH (src, mode);
       sets[i].src_volatile = do_not_record;
       sets[i].src_in_memory = hash_arg_in_memory;
       sets[i].src_in_struct = hash_arg_in_struct;
@@ -6093,7 +6508,7 @@ cse_insn (insn, in_libcall_block)
          REG_NOTE.  */
 
       if (!sets[i].src_volatile)
-       elt = lookup (src, sets[i].src_hash_code, mode);
+       elt = lookup (src, sets[i].src_hash, mode);
 
       sets[i].src_elt = elt;
 
@@ -6104,8 +6519,8 @@ cse_insn (insn, in_libcall_block)
              /* The REG_EQUAL is indicating that two formerly distinct
                 classes are now equivalent.  So merge them.  */
              merge_equiv_classes (elt, src_eqv_elt);
-             src_eqv_hash_code = HASH (src_eqv, elt->mode);
-             src_eqv_elt = lookup (src_eqv, src_eqv_hash_code, elt->mode);
+             src_eqv_hash = HASH (src_eqv, elt->mode);
+             src_eqv_elt = lookup (src_eqv, src_eqv_hash, elt->mode);
            }
 
           src_eqv_here = 0;
@@ -6117,7 +6532,7 @@ cse_insn (insn, in_libcall_block)
       /* Try to find a constant somewhere and record it in `src_const'.
         Record its table element, if any, in `src_const_elt'.  Look in
         any known equivalences first.  (If the constant is not in the
-        table, also set `sets[i].src_const_hash_code').  */
+        table, also set `sets[i].src_const_hash').  */
       if (elt)
         for (p = elt->first_same_value; p; p = p->next_same_value)
          if (p->is_const)
@@ -6143,9 +6558,8 @@ cse_insn (insn, in_libcall_block)
         hash code and look it up.  */
       if (src_const && src_const_elt == 0)
        {
-         sets[i].src_const_hash_code = HASH (src_const, mode);
-         src_const_elt = lookup (src_const, sets[i].src_const_hash_code,
-                                 mode);
+         sets[i].src_const_hash = HASH (src_const, mode);
+         src_const_elt = lookup (src_const, sets[i].src_const_hash, mode);
        }
 
       sets[i].src_const = src_const;
@@ -6261,7 +6675,53 @@ cse_insn (insn, in_libcall_block)
                }
            }
        }
-                 
+
+#ifdef LOAD_EXTEND_OP
+      /* See if a MEM has already been loaded with a widening operation;
+        if it has, we can use a subreg of that.  Many CISC machines
+        also have such operations, but this is only likely to be
+        beneficial these machines.  */
+      
+      if (flag_expensive_optimizations &&  src_related == 0
+         && (GET_MODE_SIZE (mode) < UNITS_PER_WORD)
+         && GET_MODE_CLASS (mode) == MODE_INT
+         && GET_CODE (src) == MEM && ! do_not_record
+         && LOAD_EXTEND_OP (mode) != NIL)
+       {
+         enum machine_mode tmode;
+         
+         /* Set what we are trying to extend and the operation it might
+            have been extended with.  */
+         PUT_CODE (memory_extend_rtx, LOAD_EXTEND_OP (mode));
+         XEXP (memory_extend_rtx, 0) = src;
+         
+         for (tmode = GET_MODE_WIDER_MODE (mode);
+              GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
+              tmode = GET_MODE_WIDER_MODE (tmode))
+           {
+             struct table_elt *larger_elt;
+             
+             PUT_MODE (memory_extend_rtx, tmode);
+             larger_elt = lookup (memory_extend_rtx, 
+                                  HASH (memory_extend_rtx, tmode), tmode);
+             if (larger_elt == 0)
+               continue;
+             
+             for (larger_elt = larger_elt->first_same_value;
+                  larger_elt; larger_elt = larger_elt->next_same_value)
+               if (GET_CODE (larger_elt->exp) == REG)
+                 {
+                   src_related = gen_lowpart_if_possible (mode, 
+                                                          larger_elt->exp);
+                   break;
+                 }
+             
+             if (src_related)
+               break;
+           }
+       }
+#endif /* LOAD_EXTEND_OP */
       if (src == src_folded)
         src_folded = 0;
 
@@ -6312,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))
@@ -6381,9 +6841,9 @@ cse_insn (insn, in_libcall_block)
            trial = src, src_cost = 10000;
           else if (src_eqv_cost <= src_related_cost
                   && src_eqv_cost <= src_elt_cost)
-           trial = src_eqv_here, src_eqv_cost = 10000;
+           trial = copy_rtx (src_eqv_here), src_eqv_cost = 10000;
           else if (src_related_cost <= src_elt_cost)
-           trial = src_related, src_related_cost = 10000;
+           trial = copy_rtx (src_related), src_related_cost = 10000;
           else
            {
              trial = copy_rtx (elt->exp);
@@ -6418,6 +6878,7 @@ cse_insn (insn, in_libcall_block)
                trial = gen_rtx (LABEL_REF, Pmode, get_label_after (trial));
 
              SET_SRC (sets[i].rtl) = trial;
+             cse_jumps_altered = 1;
              break;
            }
           
@@ -6441,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;
@@ -6491,11 +6956,11 @@ cse_insn (insn, in_libcall_block)
           hash_arg_in_memory = 0;
           hash_arg_in_struct = 0;
          sets[i].src = src;
-          sets[i].src_hash_code = HASH (src, mode);
+          sets[i].src_hash = HASH (src, mode);
           sets[i].src_volatile = do_not_record;
           sets[i].src_in_memory = hash_arg_in_memory;
           sets[i].src_in_struct = hash_arg_in_struct;
-          sets[i].src_elt = lookup (src, sets[i].src_hash_code, mode);
+          sets[i].src_elt = lookup (src, sets[i].src_hash, mode);
         }
 
       /* If this is a single SET, we are setting a register, and we have an
@@ -6506,7 +6971,7 @@ cse_insn (insn, in_libcall_block)
       if (n_sets == 1 && src_const && GET_CODE (dest) == REG
          && GET_CODE (src_const) != REG)
        {
-         rtx tem = find_reg_note (insn, REG_EQUAL, NULL_RTX);
+         tem = find_reg_note (insn, REG_EQUAL, NULL_RTX);
          
          /* Record the actual constant value in a REG_EQUAL note, making
             a new one if one does not already exist.  */
@@ -6578,7 +7043,7 @@ cse_insn (insn, in_libcall_block)
         before the effects of this instruction are recorded,
         since the register values used in the address computation
         are those before this instruction.  */
-      sets[i].dest_hash_code = HASH (dest, mode);
+      sets[i].dest_hash = HASH (dest, mode);
 
       /* Don't enter a bit-field in the hash table
         because the value in it after the store
@@ -6674,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.  */
@@ -6694,12 +7159,15 @@ 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), GET_MODE (dest));
          sets[i].rtl = 0;
        }
 
       if (sets[i].rtl != 0 && dest != SET_DEST (sets[i].rtl))
-       sets[i].dest_hash_code = HASH (SET_DEST (sets[i].rtl), mode);
+       sets[i].dest_hash = HASH (SET_DEST (sets[i].rtl), mode);
 
 #ifdef HAVE_cc0
       /* If setting CC0, record what it was set to, or a constant, if it
@@ -6740,8 +7208,11 @@ cse_insn (insn, in_libcall_block)
          classp = 0;
        }
       if (insert_regs (src_eqv, classp, 0))
-       src_eqv_hash_code = HASH (src_eqv, eqvmode);
-      elt = insert (src_eqv, classp, src_eqv_hash_code, 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;
       src_eqv_elt = elt;
@@ -6767,7 +7238,7 @@ cse_insn (insn, in_libcall_block)
               This is a more interesting equivalence, so we arrange later
               to treat the entire reg as the destination.  */
            sets[i].src_elt = src_eqv_elt;
-           sets[i].src_hash_code = src_eqv_hash_code;
+           sets[i].src_hash = src_eqv_hash;
          }
        else
          {
@@ -6787,8 +7258,11 @@ 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_code = HASH (src, mode);
-               elt = insert (src, classp, sets[i].src_hash_code, 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;
                sets[i].src_elt = classp = elt;
@@ -6798,7 +7272,7 @@ cse_insn (insn, in_libcall_block)
                && src != sets[i].src_const
                && ! rtx_equal_p (sets[i].src_const, src))
              sets[i].src_elt = insert (sets[i].src_const, classp,
-                                       sets[i].src_const_hash_code, mode);
+                                       sets[i].src_const_hash, mode);
          }
       }
     else if (sets[i].src_elt == 0)
@@ -6829,15 +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), GET_MODE (dest));
       }
 
   /* Make sure registers mentioned in destinations
@@ -6894,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,
@@ -6906,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_code = 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_code, GET_MODE (dest));
-       elt->in_memory = GET_CODE (sets[i].inner_dest) == MEM;
+                     sets[i].dest_hash, GET_MODE (dest));
+       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
@@ -6938,8 +7435,9 @@ cse_insn (insn, in_libcall_block)
           already entered SRC and DEST of the SET in the table.  */
 
        if (GET_CODE (dest) == SUBREG
-           && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) / UNITS_PER_WORD
-               == GET_MODE_SIZE (GET_MODE (dest)) / UNITS_PER_WORD)
+           && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) - 1)
+                / UNITS_PER_WORD)
+               == (GET_MODE_SIZE (GET_MODE (dest)) - 1)/ UNITS_PER_WORD)
            && (GET_MODE_SIZE (GET_MODE (dest))
                >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))))
            && sets[i].src_elt != 0)
@@ -6951,7 +7449,7 @@ cse_insn (insn, in_libcall_block)
                 elt = elt->next_same_value)
              {
                rtx new_src = 0;
-               int src_hash;
+               unsigned src_hash;
                struct table_elt *src_elt;
 
                /* Ignore invalid entries.  */
@@ -6971,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;
@@ -7041,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);
        }
     }
 
@@ -7089,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)
@@ -7107,10 +7614,15 @@ note_mem_written (written, writes_ptr)
             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;
        }
@@ -7145,16 +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
-         && (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
-             || (GET_CODE (ref) == MEM && ! w->all)))
-       invalidate (ref);
+
+      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)
     {
@@ -7165,10 +7680,15 @@ invalidate_from_clobbers (w, x)
          if (GET_CODE (y) == CLOBBER)
            {
              rtx ref = XEXP (y, 0);
-             if (ref
-                 &&(GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
-                    || (GET_CODE (ref) == MEM && !w->all)))
-               invalidate (ref);
+             if (ref)
+               {
+                 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));
+               }
            }
        }
     }
@@ -7218,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,
@@ -7295,8 +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), 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).
@@ -7337,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);
 
@@ -7353,9 +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), GET_MODE (dest));
 }
 
 /* Invalidate all insns from START up to the end of the function or the
@@ -7503,11 +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), GET_MODE (SET_DEST (x)));
 }
 \f
 /* Find the end of INSN's basic block and return its range,
@@ -7748,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;
 
@@ -7770,9 +8303,16 @@ cse_main (f, nregs, after_loop, file)
   reg_in_table = (int *) alloca (nregs * sizeof (int));
   reg_tick = (int *) alloca (nregs * sizeof (int));
 
+#ifdef LOAD_EXTEND_OP
+
+  /* Allocate scratch rtl here.  cse_insn will fill in the memory reference
+     and change the code and mode as appropriate.  */
+  memory_extend_rtx = gen_rtx (ZERO_EXTEND, VOIDmode, 0);
+#endif
+
   /* Discard all the free elements of the previous function
      since they are allocated in the temporarily obstack.  */
-  bzero (table, sizeof table);
+  bzero ((char *) table, sizeof table);
   free_element_chain = 0;
   n_elements_made = 0;
 
@@ -7780,7 +8320,7 @@ cse_main (f, nregs, after_loop, file)
 
   max_uid = get_max_uid ();
   uid_cuid = (int *) alloca ((max_uid + 1) * sizeof (int));
-  bzero (uid_cuid, (max_uid + 1) * sizeof (int));
+  bzero ((char *) uid_cuid, (max_uid + 1) * sizeof (int));
 
   /* Compute the mapping from uids to cuids.
      CUIDs are numbers assigned to insns, like uids,
@@ -7822,7 +8362,7 @@ cse_main (f, nregs, after_loop, file)
 #if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
         && ! (i == ARG_POINTER_REGNUM && fixed_regs[i])
 #endif
-#ifdef PIC_OFFSET_TABLE_REGNUM
+#if defined (PIC_OFFSET_TABLE_REGNUM) && !defined (PIC_OFFSET_TABLE_REG_CALL_CLOBBERED)
         && ! (i == PIC_OFFSET_TABLE_REGNUM && flag_pic)
 #endif
         )
@@ -7896,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
@@ -8032,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);
@@ -8094,22 +8637,31 @@ cse_basic_block (from, to, next_branch, around_loop)
 \f
 /* Count the number of times registers are used (not set) in X.
    COUNTS is an array in which we accumulate the count, INCR is how much
-   we count each register usage.  */
+   we count each register usage.  
+
+   Don't count a usage of DEST, which is the SET_DEST of a SET which 
+   contains X in its SET_SRC.  This is because such a SET does not
+   modify the liveness of DEST.  */
 
 static void
-count_reg_usage (x, counts, incr)
+count_reg_usage (x, counts, dest, incr)
      rtx x;
      int *counts;
+     rtx dest;
      int incr;
 {
-  enum rtx_code code = GET_CODE (x);
+  enum rtx_code code;
   char *fmt;
   int i, j;
 
-  switch (code)
+  if (x == 0)
+    return;
+
+  switch (code = GET_CODE (x))
     {
     case REG:
-      counts[REGNO (x)] += incr;
+      if (x != dest)
+       counts[REGNO (x)] += incr;
       return;
 
     case PC:
@@ -8125,28 +8677,39 @@ count_reg_usage (x, counts, incr)
     case SET:
       /* Unless we are setting a REG, count everything in SET_DEST.  */
       if (GET_CODE (SET_DEST (x)) != REG)
-       count_reg_usage (SET_DEST (x), counts, incr);
-      count_reg_usage (SET_SRC (x), counts, incr);
+       count_reg_usage (SET_DEST (x), counts, NULL_RTX, incr);
+
+      /* If SRC has side-effects, then we can't delete this insn, so the
+        usage of SET_DEST inside SRC counts.
+
+        ??? Strictly-speaking, we might be preserving this insn
+        because some other SET has side-effects, but that's hard
+        to do and can't happen now.  */
+      count_reg_usage (SET_SRC (x), counts,
+                      side_effects_p (SET_SRC (x)) ? NULL_RTX : SET_DEST (x),
+                      incr);
       return;
 
+    case CALL_INSN:
+      count_reg_usage (CALL_INSN_FUNCTION_USAGE (x), counts, NULL_RTX, incr);
+
+      /* ... falls through ...  */
     case INSN:
     case JUMP_INSN:
-    case CALL_INSN:
-      count_reg_usage (PATTERN (x), counts, incr);
+      count_reg_usage (PATTERN (x), counts, NULL_RTX, incr);
 
       /* Things used in a REG_EQUAL note aren't dead since loop may try to
         use them.  */
 
-      if (REG_NOTES (x))
-       count_reg_usage (REG_NOTES (x), counts, incr);
+      count_reg_usage (REG_NOTES (x), counts, NULL_RTX, incr);
       return;
 
     case EXPR_LIST:
     case INSN_LIST:
-      if (REG_NOTE_KIND (x) == REG_EQUAL)
-       count_reg_usage (XEXP (x, 0), counts, incr);
-      if (XEXP (x, 1))
-       count_reg_usage (XEXP (x, 1), counts, incr);
+      if (REG_NOTE_KIND (x) == REG_EQUAL
+         || GET_CODE (XEXP (x,0)) == USE)
+       count_reg_usage (XEXP (x, 0), counts, NULL_RTX, incr);
+      count_reg_usage (XEXP (x, 1), counts, NULL_RTX, incr);
       return;
     }
 
@@ -8154,10 +8717,10 @@ count_reg_usage (x, counts, incr)
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
     {
       if (fmt[i] == 'e')
-       count_reg_usage (XEXP (x, i), counts, incr);
+       count_reg_usage (XEXP (x, i), counts, dest, incr);
       else if (fmt[i] == 'E')
        for (j = XVECLEN (x, i) - 1; j >= 0; j--)
-         count_reg_usage (XVECEXP (x, i, j), counts, incr);
+         count_reg_usage (XVECEXP (x, i, j), counts, dest, incr);
     }
 }
 \f
@@ -8181,9 +8744,9 @@ delete_dead_from_cse (insns, nreg)
   int in_libcall = 0;
 
   /* First count the number of times each register is used.  */
-  bzero (counts, sizeof (int) * nreg);
+  bzero ((char *) counts, sizeof (int) * nreg);
   for (insn = next_real_insn (insns); insn; insn = next_real_insn (insn))
-    count_reg_usage (insn, counts, 1);
+    count_reg_usage (insn, counts, NULL_RTX, 1);
 
   /* Go from the last insn to the first and delete insns that only set unused
      registers or copy a register to itself.  As we delete an insn, remove
@@ -8258,7 +8821,7 @@ delete_dead_from_cse (insns, nreg)
 
       if (! live_insn)
        {
-         count_reg_usage (insn, counts, -1);
+         count_reg_usage (insn, counts, NULL_RTX, -1);
          delete_insn (insn);
        }