OSDN Git Service

Don't include sys/file.h for winnt; use process.h instead.
[pf3gnuchains/gcc-fork.git] / gcc / cse.c
index 2f37876..9bbe8a6 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, 93, 1994 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -348,6 +348,12 @@ static int cse_jumps_altered;
 
 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 +424,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 +434,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 +445,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,16 +458,23 @@ 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))
 
+/* 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 (REGNO (X)) ? 0                                \
+   ? (CHEAP_REG (X) ? 0                                        \
       : REGNO (X) >= FIRST_PSEUDO_REGISTER ? 1         \
       : 2)                                             \
    : rtx_cost (X, SET) * 2)
@@ -598,23 +609,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 +658,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 +724,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 +763,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 +783,7 @@ new_basic_block ()
        }
     }
 
-  bzero (table, sizeof table);
+  bzero ((char *) table, sizeof table);
 
   prev_insn = 0;
 
@@ -867,14 +881,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 +1087,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 +1167,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 +1186,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 +1265,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;
@@ -1377,7 +1393,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 +1445,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;
 
@@ -1464,14 +1480,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 +1507,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
@@ -1546,7 +1566,7 @@ invalidate (x)
     {
       if (GET_CODE (SUBREG_REG (x)) != REG)
        abort ();
-      invalidate (SUBREG_REG (x));
+      invalidate (SUBREG_REG (x), VOIDmode);
       return;
     }
 
@@ -1557,7 +1577,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 +1628,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 +1702,7 @@ invalidate_for_call ()
 {
   int regno, endregno;
   int i;
-  int hash;
+  unsigned hash;
   struct table_elt *p, *next;
   int in_table = 0;
 
@@ -1695,7 +1718,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 +1837,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;
 
@@ -1855,37 +1878,38 @@ 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;
-       for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++)
-         {
-           int tem = XINT (x, i);
-           hash += ((1 << HASHBITS) - 1) & (tem + (tem >> HASHBITS));
-         }
-      }
+      hash += (unsigned) code + (unsigned) GET_MODE (x);
+      for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++)
+       {
+         unsigned tem = XINT (x, i);
+         hash += tem;
+       }
       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))
@@ -1900,7 +1924,7 @@ canon_hash (x, mode)
        }
       /* 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,7 +1948,7 @@ 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--)
     {
@@ -1958,18 +1982,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 +2000,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 +2008,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 +2085,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 +2237,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 +2250,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;
@@ -2253,20 +2277,64 @@ set_nonvarying_address_components (addr, size, pbase, pstart, pend)
       base = qty_const[reg_qty[REGNO (XEXP (base, 0))]];
     }
 
-  /* 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);
+  /* 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;
 
-  /* Strip off CONST.  */
-  if (GET_CODE (base) == CONST)
-    base = XEXP (base, 0);
+       case CONST:
+         /* Strip off CONST.  */
+         base = XEXP (base, 0);
+         continue;
 
-  if (GET_CODE (base) == PLUS
-      && GET_CODE (XEXP (base, 1)) == CONST_INT)
+       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.  */
+
+             size = *pend - *pstart - INTVAL (XEXP (base, 1)) - 1;
+             start += *pstart - INTVAL (XEXP (base, 1)) - 1;
+             base = *pbase;
+           }
+         break;
+       }
+
+      break;
+    }
+
+  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 +2367,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;
@@ -2501,9 +2562,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
@@ -2539,7 +2600,7 @@ find_best_addr (insn, loc)
      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 +2609,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 +2688,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 +2711,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 +2934,67 @@ 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);
 #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 */
+
       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 (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);
 #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 */
+
       return CONST_DOUBLE_FROM_REAL_VALUE (d, mode);
     }
 #endif
@@ -3027,7 +3093,7 @@ simplify_unary_operation (code, mode, op, op_mode)
 
   /* 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
+  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 +3130,7 @@ 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.  */
          break;
 
        case ZERO_EXTEND:
@@ -3157,11 +3220,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;
@@ -3311,20 +3377,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);
@@ -3406,7 +3476,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 +3489,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 +3516,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 +3530,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 +3617,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 +3629,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 +3652,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,7 +3723,7 @@ 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));
          break;
 
@@ -3554,7 +3737,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 +3748,11 @@ 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
+             && ! rtx_equal_function_value_matters)
            return gen_rtx (ASHIFT, mode, op0, GEN_INT (val));
 
          if (GET_CODE (op1) == CONST_DOUBLE
@@ -3654,32 +3839,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;
 
@@ -3707,7 +3895,6 @@ simplify_binary_operation (code, mode, op0, op1)
 
          /* ... fall through ... */
 
-       case LSHIFT:
        case ASHIFT:
        case ASHIFTRT:
        case LSHIFTRT:
@@ -3842,28 +4029,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 +4053,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.
@@ -3963,7 +4140,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 +4335,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 +4348,165 @@ 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, we can only do this if A - B is a
+     constant integer, and then we have to compare that integer with zero as a
+     signed comparison.  Note that this will give the incorrect result from
+     comparisons that overflow.  Since these are undefined, this is probably
+     OK.  If it causes a problem, we can check for A or B being an address
+     (fp + const or SYMBOL_REF) and only do it in that case.  */
+
+  if (INTEGRAL_MODE_P (mode) && op1 != const0_rtx
+      && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == CONST_INT)
+           && (GET_CODE (op1) == REG || GET_CODE (op1) == CONST_INT))
+      && 0 != (tem = simplify_binary_operation (MINUS, mode, op0, op1))
+      && (GET_CODE (tem) == CONST_INT
+         || (code != GTU && code != GEU &&
+             code != LTU && code != LEU)))
+    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;
+       }
 
-         uh0 = h0, ul0 = l0, uh1 = h1, ul1 = l1;
+      /* 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;
 
-         equal = (h0 == h1 && l0 == l1);
-         op0lt = (h0 < h1 || (h0 == h1 && l0 < l1));
-         op0ltu = (uh0 < uh1 || (uh0 == uh1 && ul0 < ul1));
+      if (width != 0 && width < HOST_BITS_PER_WIDE_INT)
+       {
+         l0u &= ((HOST_WIDE_INT) 1 << width) - 1;
+         l1u &= ((HOST_WIDE_INT) 1 << width) - 1;
 
-         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 (l0s & ((HOST_WIDE_INT) 1 << (width - 1)))
+           l0s |= ((HOST_WIDE_INT) (-1) << width);
+
+         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 +4515,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 +4530,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,
@@ -4729,7 +4853,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);
                }
@@ -4863,7 +5008,11 @@ 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);
                  }
              }
          }
@@ -5036,13 +5185,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 +5222,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
@@ -5103,7 +5266,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 +5332,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 +5362,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 +5377,62 @@ 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 negative constant since we might then alternate between
+            chosing positive and negative constants.  Having the positive
+            constant previously-used is the more common case.  */
+         if (const_arg1 && GET_CODE (const_arg1) == CONST_INT
+             && INTVAL (const_arg1) < 0 && GET_CODE (folded_arg1) == REG)
+           {
+             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;
 
@@ -5505,7 +5720,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 +5792,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 +5802,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 +5810,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 +5846,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 +5864,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 +5872,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 +5900,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 +5913,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 +5944,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 +5963,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 +5975,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 +5990,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 +6002,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));
@@ -5808,7 +6037,7 @@ cse_insn (insn, in_libcall_block)
          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 +6062,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 +6087,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 +6143,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 destinatation.  */
+  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
@@ -6009,12 +6246,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;
@@ -6057,7 +6294,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 +6330,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 +6341,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 +6354,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 +6380,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 +6497,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;
 
@@ -6381,9 +6663,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 +6700,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;
            }
           
@@ -6491,11 +6774,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 +6789,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 +6861,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
@@ -6694,12 +6977,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 +7026,8 @@ 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);
+       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 +7053,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 +7073,8 @@ 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);
+                 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 +7084,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 +7115,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
@@ -6908,10 +7200,10 @@ cse_insn (insn, in_libcall_block)
          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));
+           sets[i].dest_hash = HASH (dest, GET_MODE (dest));
 
        elt = insert (dest, sets[i].src_elt,
-                     sets[i].dest_hash_code, GET_MODE (dest));
+                     sets[i].dest_hash, GET_MODE (dest));
        elt->in_memory = GET_CODE (sets[i].inner_dest) == MEM;
        if (elt->in_memory)
          {
@@ -6938,8 +7230,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 +7244,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.  */
@@ -7101,6 +7394,9 @@ note_mem_written (written, writes_ptr)
        }
       else if (GET_MODE (written) == BLKmode)
        *writes_ptr = everything;
+      /* (mem (scratch)) means clobber everything.  */
+      else if (GET_CODE (addr) == SCRATCH)
+       *writes_ptr = everything;
       else if (cse_rtx_addr_varies_p (written))
        {
          /* A varying address that is a sum indicates an array element,
@@ -7145,16 +7441,21 @@ 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 (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 +7466,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));
+               }
            }
        }
     }
@@ -7295,8 +7601,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).
@@ -7355,7 +7665,10 @@ invalidate_skipped_set (dest, set)
 
   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
@@ -7507,7 +7820,10 @@ cse_set_around_loop (x, insn, loop_start)
   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,
@@ -7770,9 +8086,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 +8103,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 +8145,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
         )
@@ -8094,22 +8417,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 +8457,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 +8497,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 +8524,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 +8601,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);
        }