OSDN Git Service

Make `solaris' reflect the most recent major release.
[pf3gnuchains/gcc-fork.git] / gcc / combine.c
index ba100ad..5b44d66 100644 (file)
@@ -1,5 +1,5 @@
 /* Optimize by combining instructions for GNU compiler.
-   Copyright (C) 1987, 1988, 1992 Free Software Foundation, Inc.
+   Copyright (C) 1987, 1988, 1992, 1993 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -73,13 +73,15 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
    also use the condition code; but in practice such insns would not
    combine anyway.  */
 
+#include "config.h"
+/* Must precede rtl.h for FFS.  */
 #include <stdio.h>
 
-#include "config.h"
 #include "gvarargs.h"
 #include "rtl.h"
 #include "flags.h"
 #include "regs.h"
+#include "hard-reg-set.h"
 #include "expr.h"
 #include "basic-block.h"
 #include "insn-config.h"
@@ -93,6 +95,20 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
    Use gen_lowpart_for_combine instead.  See comments there.  */
 #define gen_lowpart dont_use_gen_lowpart_you_dummy
 
+/* If byte loads either zero- or sign- extend, define BYTE_LOADS_EXTEND
+   for cases when we don't care which is true.  Define LOAD_EXTEND to
+   be ZERO_EXTEND or SIGN_EXTEND, depending on which was defined.  */
+
+#ifdef BYTE_LOADS_ZERO_EXTEND
+#define BYTE_LOADS_EXTEND
+#define LOAD_EXTEND ZERO_EXTEND
+#endif
+
+#ifdef BYTE_LOADS_SIGN_EXTEND
+#define BYTE_LOADS_EXTEND
+#define LOAD_EXTEND SIGN_EXTEND
+#endif
+
 /* Number of attempts to combine instructions in this function.  */
 
 static int combine_attempts;
@@ -223,12 +239,12 @@ static rtx *reg_last_set_value;
 /* Record the value of label_tick when the value for register n is placed in
    reg_last_set_value[n].  */
 
-static short *reg_last_set_label;
+static int *reg_last_set_label;
 
 /* Record the value of label_tick when an expression involving register n
    is placed in reg_last_set_value. */
 
-static short *reg_last_set_table_tick;
+static int *reg_last_set_table_tick;
 
 /* Set non-zero if references to register n in expressions should not be
    used.  */
@@ -237,31 +253,45 @@ static char *reg_last_set_invalid;
 
 /* Incremented for each label. */
 
-static short label_tick;
+static int label_tick;
 
 /* Some registers that are set more than once and used in more than one
    basic block are nevertheless always set in similar ways.  For example,
    a QImode register may be loaded from memory in two places on a machine
    where byte loads zero extend.
 
-   We record in the following array what we know about the significant
+   We record in the following array what we know about the nonzero
    bits of a register, specifically which bits are known to be zero.
 
    If an entry is zero, it means that we don't know anything special.  */
 
-static HOST_WIDE_INT *reg_significant;
+static unsigned HOST_WIDE_INT *reg_nonzero_bits;
 
-/* Mode used to compute significance in reg_significant.  It is the largest
+/* Mode used to compute significance in reg_nonzero_bits.  It is the largest
    integer mode that can fit in HOST_BITS_PER_WIDE_INT.  */
 
-static enum machine_mode significant_mode;
+static enum machine_mode nonzero_bits_mode;
 
-/* Nonzero when reg_significant can be safely used.  It is zero while
-   computing reg_significant.  This prevents propagating values based
-   on previously set values, which can be incorrect if a variable
-   is modified in a loop.  */
+/* Nonzero if we know that a register has some leading bits that are always
+   equal to the sign bit.  */
 
-static int significant_valid;
+static char *reg_sign_bit_copies;
+
+/* Nonzero when reg_nonzero_bits and reg_sign_bit_copies can be safely used.
+   It is zero while computing them and after combine has completed.  This
+   former test prevents propagating values based on previously set values,
+   which can be incorrect if a variable is modified in a loop.  */
+
+static int nonzero_sign_valid;
+
+/* These arrays are maintained in parallel with reg_last_set_value
+   and are used to store the mode in which the register was last set,
+   the bits that were known to be zero when it was last set, and the
+   number of sign bits copies it was known to have when it was last set.  */
+
+static enum machine_mode *reg_last_set_mode;
+static unsigned HOST_WIDE_INT *reg_last_set_nonzero_bits;
+static char *reg_last_set_sign_bit_copies;
 \f
 /* Record one modification to rtl structure
    to be undone by storing old_contents into *where.
@@ -269,16 +299,9 @@ static int significant_valid;
 
 struct undo
 {
-  rtx *where;
-  rtx old_contents;
-  int is_int;
-};
-
-struct undo_int
-{
-  int *where;
-  int old_contents;
   int is_int;
+  union {rtx rtx; int i;} old_contents;
+  union {rtx *rtx; int *i;} where;
 };
 
 /* Record a bunch of changes to be undone, up to MAX_UNDO of them.
@@ -312,11 +335,11 @@ static struct undobuf undobuf;
  do { rtx _new = (NEWVAL);                                             \
       if (undobuf.num_undo < MAX_UNDO)                                 \
        {                                                               \
-         undobuf.undo[undobuf.num_undo].where = &INTO;                 \
-         undobuf.undo[undobuf.num_undo].old_contents = INTO;           \
          undobuf.undo[undobuf.num_undo].is_int = 0;                    \
+         undobuf.undo[undobuf.num_undo].where.rtx = &INTO;             \
+         undobuf.undo[undobuf.num_undo].old_contents.rtx = INTO;       \
          INTO = _new;                                                  \
-         if (undobuf.undo[undobuf.num_undo].old_contents != INTO)      \
+         if (undobuf.undo[undobuf.num_undo].old_contents.rtx != INTO)  \
            undobuf.num_undo++;                                         \
        }                                                               \
     } while (0)
@@ -328,13 +351,11 @@ static struct undobuf undobuf;
 #define SUBST_INT(INTO, NEWVAL)  \
  do { if (undobuf.num_undo < MAX_UNDO)                                 \
 {                                                                      \
-         struct undo_int *u                                            \
-           = (struct undo_int *)&undobuf.undo[undobuf.num_undo];       \
-         u->where = (int *) &INTO;                                     \
-         u->old_contents = INTO;                                       \
-         u->is_int = 1;                                                \
+         undobuf.undo[undobuf.num_undo].is_int = 1;                    \
+         undobuf.undo[undobuf.num_undo].where.i = (int *) &INTO;       \
+         undobuf.undo[undobuf.num_undo].old_contents.i = INTO;         \
          INTO = NEWVAL;                                                \
-         if (u->old_contents != INTO)                                  \
+         if (undobuf.undo[undobuf.num_undo].old_contents.i != INTO)    \
            undobuf.num_undo++;                                         \
        }                                                               \
      } while (0)
@@ -344,7 +365,8 @@ static struct undobuf undobuf;
 
 static int n_occurrences;
 
-static void set_significant ();
+static void set_nonzero_bits_and_sign_copies ();
+static void setup_incoming_promotions ();
 static void move_deaths ();
 rtx remove_death ();
 static void record_value_for_reg ();
@@ -360,11 +382,13 @@ static rtx expand_field_assignment ();
 static rtx make_extraction ();
 static int get_pos_from_mask ();
 static rtx force_to_mode ();
+static rtx known_cond ();
 static rtx make_field_assignment ();
 static rtx make_compound_operation ();
 static rtx apply_distributive_law ();
 static rtx simplify_and_const_int ();
-static unsigned HOST_WIDE_INT significant_bits ();
+static unsigned HOST_WIDE_INT nonzero_bits ();
+static int num_sign_bit_copies ();
 static int merge_outer_ops ();
 static rtx simplify_shift_const ();
 static int recog_for_combine ();
@@ -395,23 +419,38 @@ combine_instructions (f, nregs)
   combine_merges = 0;
   combine_extras = 0;
   combine_successes = 0;
+  undobuf.num_undo = previous_num_undos = 0;
 
   combine_max_regno = nregs;
 
   reg_last_death = (rtx *) alloca (nregs * sizeof (rtx));
   reg_last_set = (rtx *) alloca (nregs * sizeof (rtx));
   reg_last_set_value = (rtx *) alloca (nregs * sizeof (rtx));
-  reg_last_set_table_tick = (short *) alloca (nregs * sizeof (short));
-  reg_last_set_label = (short *) alloca (nregs * sizeof (short));
+  reg_last_set_table_tick = (int *) alloca (nregs * sizeof (int));
+  reg_last_set_label = (int *) alloca (nregs * sizeof (int));
   reg_last_set_invalid = (char *) alloca (nregs * sizeof (char));
-  reg_significant = (HOST_WIDE_INT *) alloca (nregs * sizeof (HOST_WIDE_INT));
+  reg_last_set_mode
+    = (enum machine_mode *) alloca (nregs * sizeof (enum machine_mode));
+  reg_last_set_nonzero_bits
+    = (unsigned HOST_WIDE_INT *) alloca (nregs * sizeof (HOST_WIDE_INT));
+  reg_last_set_sign_bit_copies
+    = (char *) alloca (nregs * sizeof (char));
+
+  reg_nonzero_bits
+    = (unsigned HOST_WIDE_INT *) alloca (nregs * sizeof (HOST_WIDE_INT));
+  reg_sign_bit_copies = (char *) alloca (nregs * sizeof (char));
 
   bzero (reg_last_death, nregs * sizeof (rtx));
   bzero (reg_last_set, nregs * sizeof (rtx));
   bzero (reg_last_set_value, nregs * sizeof (rtx));
-  bzero (reg_last_set_table_tick, nregs * sizeof (short));
+  bzero (reg_last_set_table_tick, nregs * sizeof (int));
+  bzero (reg_last_set_label, nregs * sizeof (int));
   bzero (reg_last_set_invalid, nregs * sizeof (char));
-  bzero (reg_significant, nregs * sizeof (HOST_WIDE_INT));
+  bzero (reg_last_set_mode, nregs * sizeof (enum machine_mode));
+  bzero (reg_last_set_nonzero_bits, nregs * sizeof (HOST_WIDE_INT));
+  bzero (reg_last_set_sign_bit_copies, nregs * sizeof (char));
+  bzero (reg_nonzero_bits, nregs * sizeof (HOST_WIDE_INT));
+  bzero (reg_sign_bit_copies, nregs * sizeof (char));
 
   init_recog_no_volatile ();
 
@@ -423,34 +462,59 @@ combine_instructions (f, nregs)
 
   uid_cuid = (int *) alloca ((i + 1) * sizeof (int));
 
-  significant_mode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0);
+  nonzero_bits_mode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0);
 
-  /* Don't use reg_significant when computing it.  This can cause problems
+  /* Don't use reg_nonzero_bits when computing it.  This can cause problems
      when, for example, we have j <<= 1 in a loop.  */
 
-  significant_valid = 0;
+  nonzero_sign_valid = 0;
 
   /* Compute the mapping from uids to cuids.
      Cuids are numbers assigned to insns, like uids,
      except that cuids increase monotonically through the code. 
 
      Scan all SETs and see if we can deduce anything about what
-     bits are significant for some registers.  */
+     bits are known to be zero for some registers and how many copies
+     of the sign bit are known to exist for those registers.
+
+     Also set any known values so that we can use it while searching
+     for what bits are known to be set.  */
+
+  label_tick = 1;
+
+  setup_incoming_promotions ();
 
   for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
     {
       INSN_CUID (insn) = ++i;
+      subst_low_cuid = i;
+      subst_insn = insn;
+
       if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
-       note_stores (PATTERN (insn), set_significant);
+       {
+         note_stores (PATTERN (insn), set_nonzero_bits_and_sign_copies);
+         record_dead_and_set_regs (insn);
+       }
+
+      if (GET_CODE (insn) == CODE_LABEL)
+       label_tick++;
     }
 
-  significant_valid = 1;
+  nonzero_sign_valid = 1;
 
   /* Now scan all the insns in forward order.  */
 
   label_tick = 1;
   last_call_cuid = 0;
   mem_last_set = 0;
+  bzero (reg_last_death, nregs * sizeof (rtx));
+  bzero (reg_last_set, nregs * sizeof (rtx));
+  bzero (reg_last_set_value, nregs * sizeof (rtx));
+  bzero (reg_last_set_table_tick, nregs * sizeof (int));
+  bzero (reg_last_set_label, nregs * sizeof (int));
+  bzero (reg_last_set_invalid, nregs * sizeof (char));
+
+  setup_incoming_promotions ();
 
   for (insn = f; insn; insn = next ? next : NEXT_INSN (insn))
     {
@@ -554,39 +618,117 @@ combine_instructions (f, nregs)
   total_merges += combine_merges;
   total_extras += combine_extras;
   total_successes += combine_successes;
+
+  nonzero_sign_valid = 0;
+}
+\f
+/* Set up any promoted values for incoming argument registers.  */
+
+static void
+setup_incoming_promotions ()
+{
+#ifdef PROMOTE_FUNCTION_ARGS
+  int regno;
+  rtx reg;
+  enum machine_mode mode;
+  int unsignedp;
+  rtx first = get_insns ();
+
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (FUNCTION_ARG_REGNO_P (regno)
+       && (reg = promoted_input_arg (regno, &mode, &unsignedp)) != 0)
+      record_value_for_reg (reg, first,
+                           gen_rtx (unsignedp ? ZERO_EXTEND : SIGN_EXTEND,
+                                    GET_MODE (reg),
+                                    gen_rtx (CLOBBER, mode, const0_rtx)));
+#endif
 }
 \f
 /* Called via note_stores.  If X is a pseudo that is used in more than
    one basic block, is narrower that HOST_BITS_PER_WIDE_INT, and is being
-   set, record what bits are significant.  If we are clobbering X,
+   set, record what bits are known zero.  If we are clobbering X,
    ignore this "set" because the clobbered value won't be used. 
 
    If we are setting only a portion of X and we can't figure out what
    portion, assume all bits will be used since we don't know what will
-   be happening.  */
+   be happening.
+
+   Similarly, set how many bits of X are known to be copies of the sign bit
+   at all locations in the function.  This is the smallest number implied 
+   by any set of X.  */
 
 static void
-set_significant (x, set)
+set_nonzero_bits_and_sign_copies (x, set)
      rtx x;
      rtx set;
 {
+  int num;
+
   if (GET_CODE (x) == REG
       && REGNO (x) >= FIRST_PSEUDO_REGISTER
       && reg_n_sets[REGNO (x)] > 1
       && reg_basic_block[REGNO (x)] < 0
+      /* If this register is undefined at the start of the file, we can't
+        say what its contents were.  */
+      && ! (basic_block_live_at_start[0][REGNO (x) / REGSET_ELT_BITS]
+           & ((REGSET_ELT_TYPE) 1 << (REGNO (x) % REGSET_ELT_BITS)))
       && GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT)
     {
       if (GET_CODE (set) == CLOBBER)
-       return;
+       {
+         reg_nonzero_bits[REGNO (x)] = GET_MODE_MASK (GET_MODE (x));
+         reg_sign_bit_copies[REGNO (x)] = 0;
+         return;
+       }
 
       /* If this is a complex assignment, see if we can convert it into a
         simple assignment.  */
       set = expand_field_assignment (set);
-      if (SET_DEST (set) == x)
-       reg_significant[REGNO (x)]
-         |= significant_bits (SET_SRC (set), significant_mode);
+
+      /* If this is a simple assignment, or we have a paradoxical SUBREG,
+        set what we know about X.  */
+
+      if (SET_DEST (set) == x
+         || (GET_CODE (SET_DEST (set)) == SUBREG
+             && (GET_MODE_SIZE (GET_MODE (SET_DEST (set)))
+                 > GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_DEST (set)))))
+             && SUBREG_REG (SET_DEST (set)) == x))
+       {
+         rtx src = SET_SRC (set);
+
+#ifdef SHORT_IMMEDIATES_SIGN_EXTEND
+         /* If X is narrower than a word and SRC is a non-negative
+            constant that would appear negative in the mode of X,
+            sign-extend it for use in reg_nonzero_bits because some
+            machines (maybe most) will actually do the sign-extension
+            and this is the conservative approach. 
+
+            ??? For 2.5, try to tighten up the MD files in this regard
+            instead of this kludge.  */
+
+         if (GET_MODE_BITSIZE (GET_MODE (x)) < BITS_PER_WORD
+             && GET_CODE (src) == CONST_INT
+             && INTVAL (src) > 0
+             && 0 != (INTVAL (src)
+                      & ((HOST_WIDE_INT) 1
+                         << GET_MODE_BITSIZE (GET_MODE (x)))))
+           src = GEN_INT (INTVAL (src)
+                          | ((HOST_WIDE_INT) (-1)
+                             << GET_MODE_BITSIZE (GET_MODE (x))));
+#endif
+
+         reg_nonzero_bits[REGNO (x)]
+           |= nonzero_bits (src, nonzero_bits_mode);
+         num = num_sign_bit_copies (SET_SRC (set), GET_MODE (x));
+         if (reg_sign_bit_copies[REGNO (x)] == 0
+             || reg_sign_bit_copies[REGNO (x)] > num)
+           reg_sign_bit_copies[REGNO (x)] = num;
+       }
       else
-       reg_significant[REGNO (x)] = GET_MODE_MASK (GET_MODE (x));
+       {
+         reg_nonzero_bits[REGNO (x)] = GET_MODE_MASK (GET_MODE (x));
+         reg_sign_bit_copies[REGNO (x)] = 0;
+       }
     }
 }
 \f
@@ -716,10 +858,12 @@ can_combine_p (insn, i3, pred, succ, pdest, psrc)
         If the insns are adjacent, a use can't cross a set even though we
         think it might (this can happen for a sequence of insns each setting
         the same destination; reg_last_set of that register might point to
-        a NOTE).  Also, don't move a volatile asm across any other insns.  */
+        a NOTE).  Also, don't move a volatile asm or UNSPEC_VOLATILE across
+        any other insns.  */
       || (! all_adjacent
          && (use_crosses_set_p (src, INSN_CUID (insn))
-             || (GET_CODE (src) == ASM_OPERANDS && MEM_VOLATILE_P (src))))
+             || (GET_CODE (src) == ASM_OPERANDS && MEM_VOLATILE_P (src))
+             || GET_CODE (src) == UNSPEC_VOLATILE))
       /* If there is a REG_NO_CONFLICT note for DEST in I3 or SUCC, we get
         better register allocation by not doing the combine.  */
       || find_reg_note (i3, REG_NO_CONFLICT, dest)
@@ -760,11 +904,15 @@ can_combine_p (insn, i3, pred, succ, pdest, psrc)
   else if (GET_CODE (dest) != CC0)
     return 0;
 
-  /* Don't substitute for a register intended as a clobberable operand.  */
+  /* Don't substitute for a register intended as a clobberable operand.
+     Similarly, don't substitute an expression containing a register that
+     will be clobbered in I3.  */
   if (GET_CODE (PATTERN (i3)) == PARALLEL)
     for (i = XVECLEN (PATTERN (i3), 0) - 1; i >= 0; i--)
       if (GET_CODE (XVECEXP (PATTERN (i3), 0, i)) == CLOBBER
-         && rtx_equal_p (XEXP (XVECEXP (PATTERN (i3), 0, i), 0), dest))
+         && (reg_overlap_mentioned_p (XEXP (XVECEXP (PATTERN (i3), 0, i), 0),
+                                      src)
+             || rtx_equal_p (XEXP (XVECEXP (PATTERN (i3), 0, i), 0), dest)))
        return 0;
 
   /* If INSN contains anything volatile, or is an `asm' (whether volatile
@@ -928,9 +1076,18 @@ combinable_i3pat (i3, loc, i2dest, i1dest, i1_not_in_src, pi3dest_killed)
        return 0;
 
       /* If DEST is used in I3, it is being killed in this insn,
-        so record that for later.  */
+        so record that for later. 
+        Never add REG_DEAD notes for the FRAME_POINTER_REGNUM or the
+        STACK_POINTER_REGNUM, since these are always considered to be
+        live.  Similarly for ARG_POINTER_REGNUM if it is fixed.  */
       if (pi3dest_killed && GET_CODE (dest) == REG
-         && reg_referenced_p (dest, PATTERN (i3)))
+         && reg_referenced_p (dest, PATTERN (i3))
+         && REGNO (dest) != FRAME_POINTER_REGNUM
+#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
+         && (REGNO (dest) != ARG_POINTER_REGNUM
+             || ! fixed_regs [REGNO (dest)])
+#endif
+         && REGNO (dest) != STACK_POINTER_REGNUM)
        {
          if (*pi3dest_killed)
            return 0;
@@ -1236,7 +1393,6 @@ try_combine (i3, i2, i1)
   maxreg = max_reg_num ();
 
   subst_insn = i3;
-  subst_low_cuid = i1 ? INSN_CUID (i1) : INSN_CUID (i2);
 
   /* It is possible that the source of I2 or I1 may be performing an
      unneeded operation, such as a ZERO_EXTEND of something that is known
@@ -1258,9 +1414,15 @@ try_combine (i3, i2, i1)
         The cases that we are interested in here do not involve the few
         cases were is_replaced is checked.  */
       if (i1)
-       i1src = subst (i1src, pc_rtx, pc_rtx, 0, 0);
+       {
+         subst_low_cuid = INSN_CUID (i1);
+         i1src = subst (i1src, pc_rtx, pc_rtx, 0, 0);
+       }
       else
-       i2src = subst (i2src, pc_rtx, pc_rtx, 0, 0);
+       {
+         subst_low_cuid = INSN_CUID (i2);
+         i2src = subst (i2src, pc_rtx, pc_rtx, 0, 0);
+       }
 
       previous_num_undos = undobuf.num_undo;
     }
@@ -1333,6 +1495,7 @@ try_combine (i3, i2, i1)
         need to make a unique copy of I2SRC each time we substitute it
         to avoid self-referential rtl.  */
 
+      subst_low_cuid = INSN_CUID (i2);
       newpat = subst (PATTERN (i3), i2dest, i2src, 0,
                      ! i1_feeds_i3 && i1dest_in_i1src);
       previous_num_undos = undobuf.num_undo;
@@ -1358,6 +1521,7 @@ try_combine (i3, i2, i1)
        }
 
       n_occurrences = 0;
+      subst_low_cuid = INSN_CUID (i1);
       newpat = subst (newpat, i1dest, i1src, 0, 0);
       previous_num_undos = undobuf.num_undo;
     }
@@ -1465,6 +1629,32 @@ try_combine (i3, i2, i1)
       insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
     }
 
+  /* See if this is an XOR.  If so, perhaps the problem is that the
+     constant is out of range.  Replace it with a complemented XOR with
+     a complemented constant; it might be in range.  */
+
+  else if (insn_code_number < 0 && GET_CODE (newpat) == SET
+          && GET_CODE (SET_SRC (newpat)) == XOR
+          && GET_CODE (XEXP (SET_SRC (newpat), 1)) == CONST_INT
+          && ((temp = simplify_unary_operation (NOT,
+                                                GET_MODE (SET_SRC (newpat)),
+                                                XEXP (SET_SRC (newpat), 1),
+                                                GET_MODE (SET_SRC (newpat))))
+              != 0))
+    {
+      enum machine_mode i_mode = GET_MODE (SET_SRC (newpat));
+      rtx pat
+       = gen_rtx_combine (SET, VOIDmode, SET_DEST (newpat),
+                          gen_unary (NOT, i_mode,
+                                     gen_binary (XOR, i_mode,
+                                                 XEXP (SET_SRC (newpat), 0),
+                                                 temp)));
+
+      insn_code_number = recog_for_combine (&pat, i3, &new_i3_notes);
+      if (insn_code_number >= 0)
+       newpat = pat;
+    }
+                                                       
   /* If we were combining three insns and the result is a simple SET
      with no ASM_OPERANDS that wasn't recognized, try to split it into two
      insns.  There are two ways to do this.  It can be split using a 
@@ -1482,7 +1672,15 @@ try_combine (i3, i2, i1)
         convert I2DEST to the mode of the source of NEWPAT if we can.  */
 
       m_split = split_insns (newpat, i3);
-      if (m_split == 0)
+
+      /* We can only use I2DEST as a scratch reg if it doesn't overlap any
+        inputs of NEWPAT.  */
+
+      /* ??? If I2DEST is not safe, and I1DEST exists, then it would be
+        possible to try that as a scratch reg.  This would require adding
+        more code to make it work though.  */
+
+      if (m_split == 0 && ! reg_overlap_mentioned_p (ni2dest, newpat))
        {
          /* If I2DEST is a hard register or the only use of a pseudo,
             we can change its mode.  */
@@ -1509,8 +1707,12 @@ try_combine (i3, i2, i1)
              || ! use_crosses_set_p (PATTERN (XVECEXP (m_split, 0, 0)),
                                      INSN_CUID (i2))))
        {
+         rtx i2set, i3set;
+         rtx newi3pat = PATTERN (XVECEXP (m_split, 0, 1));
          newi2pat = PATTERN (XVECEXP (m_split, 0, 0));
-         newpat = PATTERN (XVECEXP (m_split, 0, 1));
+
+         i3set = single_set (XVECEXP (m_split, 0, 1));
+         i2set = single_set (XVECEXP (m_split, 0, 0));
 
          /* In case we changed the mode of I2DEST, replace it in the
             pseudo-register table here.  We can't do it above in case this
@@ -1520,27 +1722,30 @@ try_combine (i3, i2, i1)
            SUBST (regno_reg_rtx[REGNO (i2dest)], ni2dest);
 
          i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
-         if (i2_code_number >= 0)
-           insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
+
+         /* If I2 or I3 has multiple SETs, we won't know how to track
+            register status, so don't use these insns.  */
+
+         if (i2_code_number >= 0 && i2set && i3set)
+           insn_code_number = recog_for_combine (&newi3pat, i3,
+                                                 &new_i3_notes);
+
+         if (insn_code_number >= 0)
+           newpat = newi3pat;
 
          /* It is possible that both insns now set the destination of I3.
-            If so, we must show an extra use of it and update
-            reg_significant.  */
+            If so, we must show an extra use of it.  */
 
-         if (insn_code_number >= 0 && GET_CODE (SET_DEST (newpat)) == REG
-             && GET_CODE (SET_DEST (newi2pat)) == REG
-             && REGNO (SET_DEST (newpat)) == REGNO (SET_DEST (newi2pat)))
-           {
-             reg_n_sets[REGNO (SET_DEST (newpat))]++;
-             set_significant (SET_DEST (newi2pat), newi2pat);
-             set_significant (SET_DEST (newpat), newpat);
-           }
+         if (insn_code_number >= 0 && GET_CODE (SET_DEST (i3set)) == REG
+             && GET_CODE (SET_DEST (i2set)) == REG
+             && REGNO (SET_DEST (i3set)) == REGNO (SET_DEST (i2set)))
+           reg_n_sets[REGNO (SET_DEST (i2set))]++;
        }
 
       /* If we can split it and use I2DEST, go ahead and see if that
         helps things be recognized.  Verify that none of the registers
         are set between I2 and I3.  */
-      else if ((split = find_split_point (&newpat)) != 0
+      if (insn_code_number < 0 && (split = find_split_point (&newpat, i3)) != 0
 #ifdef HAVE_cc0
          && GET_CODE (i2dest) == REG
 #endif
@@ -1620,11 +1825,13 @@ try_combine (i3, i2, i1)
           && ! find_reg_note (i3, REG_UNUSED,
                               SET_DEST (XVECEXP (newpat, 0, 0))))
     {
+      rtx ni2dest;
+
       newi2pat = XVECEXP (newpat, 0, 0);
+      ni2dest = SET_DEST (XVECEXP (newpat, 0, 0));
       newpat = XVECEXP (newpat, 0, 1);
       SUBST (SET_SRC (newpat),
-            gen_lowpart_for_combine (GET_MODE (SET_SRC (newpat)),
-                                     SET_DEST (newi2pat)));
+            gen_lowpart_for_combine (GET_MODE (SET_SRC (newpat)), ni2dest));
       i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
       if (i2_code_number >= 0)
        insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
@@ -1661,7 +1868,7 @@ try_combine (i3, i2, i1)
               insn = NEXT_INSN (insn))
            {
              if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
-                 && reg_referenced_p (SET_DEST (newi2pat), PATTERN (insn)))
+                 && reg_referenced_p (ni2dest, PATTERN (insn)))
                {
                  for (link = LOG_LINKS (insn); link;
                       link = XEXP (link, 1))
@@ -1744,9 +1951,18 @@ try_combine (i3, i2, i1)
 
          if (REG_NOTE_KIND (note) == REG_UNUSED
              && ! reg_set_p (XEXP (note, 0), PATTERN (undobuf.other_insn)))
-           remove_note (undobuf.other_insn, note);
+           {
+             if (GET_CODE (XEXP (note, 0)) == REG)
+               reg_n_deaths[REGNO (XEXP (note, 0))]--;
+
+             remove_note (undobuf.other_insn, note);
+           }
        }
 
+      for (note = new_other_notes; note; note = XEXP (note, 1))
+       if (GET_CODE (XEXP (note, 0)) == REG)
+         reg_n_deaths[REGNO (XEXP (note, 0))]++;
+
       distribute_notes (new_other_notes, undobuf.other_insn,
                        undobuf.other_insn, NULL_RTX, NULL_RTX, NULL_RTX);
     }
@@ -1874,26 +2090,74 @@ try_combine (i3, i2, i1)
 
     /* Distribute any notes added to I2 or I3 by recog_for_combine.  We
        know these are REG_UNUSED and want them to go to the desired insn,
-       so we always pass it as i3.  */
+       so we always pass it as i3.  We have not counted the notes in 
+       reg_n_deaths yet, so we need to do so now.  */
+
     if (newi2pat && new_i2_notes)
-      distribute_notes (new_i2_notes, i2, i2, NULL_RTX, NULL_RTX, NULL_RTX);
+      {
+       for (temp = new_i2_notes; temp; temp = XEXP (temp, 1))
+         if (GET_CODE (XEXP (temp, 0)) == REG)
+           reg_n_deaths[REGNO (XEXP (temp, 0))]++;
+       
+       distribute_notes (new_i2_notes, i2, i2, NULL_RTX, NULL_RTX, NULL_RTX);
+      }
+
     if (new_i3_notes)
-      distribute_notes (new_i3_notes, i3, i3, NULL_RTX, NULL_RTX, NULL_RTX);
+      {
+       for (temp = new_i3_notes; temp; temp = XEXP (temp, 1))
+         if (GET_CODE (XEXP (temp, 0)) == REG)
+           reg_n_deaths[REGNO (XEXP (temp, 0))]++;
+       
+       distribute_notes (new_i3_notes, i3, i3, NULL_RTX, NULL_RTX, NULL_RTX);
+      }
 
     /* If I3DEST was used in I3SRC, it really died in I3.  We may need to
-       put a REG_DEAD note for it somewhere.  Similarly for I2 and I1.  */
+       put a REG_DEAD note for it somewhere.  Similarly for I2 and I1.
+       Show an additional death due to the REG_DEAD note we make here.  If
+       we discard it in distribute_notes, we will decrement it again.  */
+
     if (i3dest_killed)
-      distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i3dest_killed, NULL_RTX),
-                       NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
-                       NULL_RTX, NULL_RTX);
+      {
+       if (GET_CODE (i3dest_killed) == REG)
+         reg_n_deaths[REGNO (i3dest_killed)]++;
+
+       distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i3dest_killed,
+                                  NULL_RTX),
+                         NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
+                         NULL_RTX, NULL_RTX);
+      }
+
+    /* For I2 and I1, we have to be careful.  If NEWI2PAT exists and sets
+       I2DEST or I1DEST, the death must be somewhere before I2, not I3.  If
+       we passed I3 in that case, it might delete I2.  */
+
     if (i2dest_in_i2src)
-      distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i2dest, NULL_RTX),
-                       NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
-                       NULL_RTX, NULL_RTX);
+      {
+       if (GET_CODE (i2dest) == REG)
+         reg_n_deaths[REGNO (i2dest)]++;
+
+       if (newi2pat && reg_set_p (i2dest, newi2pat))
+         distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i2dest, NULL_RTX),
+                           NULL_RTX, i2, NULL_RTX, NULL_RTX, NULL_RTX);
+       else
+         distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i2dest, NULL_RTX),
+                           NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
+                           NULL_RTX, NULL_RTX);
+      }
+
     if (i1dest_in_i1src)
-      distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i1dest, NULL_RTX),
-                       NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
-                       NULL_RTX, NULL_RTX);
+      {
+       if (GET_CODE (i1dest) == REG)
+         reg_n_deaths[REGNO (i1dest)]++;
+
+       if (newi2pat && reg_set_p (i1dest, newi2pat))
+         distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i1dest, NULL_RTX),
+                           NULL_RTX, i2, NULL_RTX, NULL_RTX, NULL_RTX);
+       else
+         distribute_notes (gen_rtx (EXPR_LIST, REG_DEAD, i1dest, NULL_RTX),
+                           NULL_RTX, i3, newi2pat ? i2 : NULL_RTX,
+                           NULL_RTX, NULL_RTX);
+      }
 
     distribute_links (i3links);
     distribute_links (i2links);
@@ -1901,13 +2165,23 @@ try_combine (i3, i2, i1)
 
     if (GET_CODE (i2dest) == REG)
       {
-       /* The insn that previously set this register doesn't exist, and
-          this life of the register may not exist either.  Show that
-          we don't know its value any more.  If we don't do this and
-          I2 set the register to a value that depended on its old
+       rtx link;
+       rtx i2_insn = 0, i2_val = 0, set;
+
+       /* The insn that used to set this register doesn't exist, and
+          this life of the register may not exist either.  See if one of
+          I3's links points to an insn that sets I2DEST.  If it does, 
+          that is now the last known value for I2DEST. If we don't update
+          this and I2 set the register to a value that depended on its old
           contents, we will get confused.  If this insn is used, thing
           will be set correctly in combine_instructions.  */
-       record_value_for_reg (i2dest, NULL_RTX, NULL_RTX);
+
+       for (link = LOG_LINKS (i3); link; link = XEXP (link, 1))
+         if ((set = single_set (XEXP (link, 0))) != 0
+             && rtx_equal_p (i2dest, SET_DEST (set)))
+           i2_insn = XEXP (link, 0), i2_val = SET_SRC (set);
+
+       record_value_for_reg (i2dest, i2_insn, i2_val);
 
        /* If the reg formerly set in I2 died only once and that was in I3,
           zero its use count so it won't make `reload' do any work.  */
@@ -1924,7 +2198,16 @@ try_combine (i3, i2, i1)
 
     if (i1 && GET_CODE (i1dest) == REG)
       {
-       record_value_for_reg (i1dest, NULL_RTX, NULL_RTX);
+       rtx link;
+       rtx i1_insn = 0, i1_val = 0, set;
+
+       for (link = LOG_LINKS (i3); link; link = XEXP (link, 1))
+         if ((set = single_set (XEXP (link, 0))) != 0
+             && rtx_equal_p (i1dest, SET_DEST (set)))
+           i1_insn = XEXP (link, 0), i1_val = SET_SRC (set);
+
+       record_value_for_reg (i1dest, i1_insn, i1_val);
+
        regno = REGNO (i1dest);
        if (! added_sets_1)
          {
@@ -1936,12 +2219,20 @@ try_combine (i3, i2, i1)
          }
       }
 
+    /* Update reg_nonzero_bits et al for any changes that may have been made
+       to this insn.  */
+
+    note_stores (newpat, set_nonzero_bits_and_sign_copies);
+    if (newi2pat)
+      note_stores (newi2pat, set_nonzero_bits_and_sign_copies);
+
     /* If I3 is now an unconditional jump, ensure that it has a 
        BARRIER following it since it may have initially been a
-       conditional jump.  */
+       conditional jump.  It may also be the last nonnote insn.  */
 
     if ((GET_CODE (newpat) == RETURN || simplejump_p (i3))
-       && GET_CODE (next_nonnote_insn (i3)) != BARRIER)
+       && ((temp = next_nonnote_insn (i3)) == NULL_RTX
+           || GET_CODE (temp) != BARRIER))
       emit_barrier_after (i3);
   }
 
@@ -1959,21 +2250,29 @@ undo_all ()
   if (undobuf.num_undo > MAX_UNDO)
     undobuf.num_undo = MAX_UNDO;
   for (i = undobuf.num_undo - 1; i >= 0; i--)
-    *undobuf.undo[i].where = undobuf.undo[i].old_contents;
+    {
+      if (undobuf.undo[i].is_int)
+       *undobuf.undo[i].where.i = undobuf.undo[i].old_contents.i;
+      else
+       *undobuf.undo[i].where.rtx = undobuf.undo[i].old_contents.rtx;
+      
+    }
 
   obfree (undobuf.storage);
   undobuf.num_undo = 0;
 }
 \f
 /* Find the innermost point within the rtx at LOC, possibly LOC itself,
-   where we have an arithmetic expression and return that point.
+   where we have an arithmetic expression and return that point.  LOC will
+   be inside INSN.
 
    try_combine will call this function to see if an insn can be split into
    two insns.  */
 
 static rtx *
-find_split_point (loc)
+find_split_point (loc, insn)
      rtx *loc;
+     rtx insn;
 {
   rtx x = *loc;
   enum rtx_code code = GET_CODE (x);
@@ -1991,7 +2290,7 @@ find_split_point (loc)
       if (GET_CODE (SUBREG_REG (x)) == MEM)
        return loc;
 #endif
-      return find_split_point (&SUBREG_REG (x));
+      return find_split_point (&SUBREG_REG (x), insn);
 
     case MEM:
 #ifdef HAVE_lo_sum
@@ -2011,7 +2310,7 @@ find_split_point (loc)
       /* If we have a PLUS whose second operand is a constant and the
         address is not valid, perhaps will can split it up using
         the machine-specific way to split large constants.  We use
-        the first pseudo-reg (one of the virtual regs) as a placeholder;
+        the first psuedo-reg (one of the virtual regs) as a placeholder;
         it will not remain in the result.  */
       if (GET_CODE (XEXP (x, 0)) == PLUS
          && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
@@ -2060,6 +2359,17 @@ find_split_point (loc)
                  return split;
                }
            }
+         
+         /* If that didn't work, perhaps the first operand is complex and
+            needs to be computed separately, so make a split point there.
+            This will occur on machines that just support REG + CONST
+            and have a constant moved through some previous computation.  */
+
+         else if (GET_RTX_CLASS (GET_CODE (XEXP (XEXP (x, 0), 0))) != 'o'
+                  && ! (GET_CODE (XEXP (XEXP (x, 0), 0)) == SUBREG
+                        && (GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (XEXP (x, 0), 0))))
+                            == 'o')))
+           return &XEXP (XEXP (x, 0), 0);
        }
       break;
 
@@ -2080,7 +2390,7 @@ find_split_point (loc)
 #endif
 
       /* See if we can split SET_SRC as it stands.  */
-      split = find_split_point (&SET_SRC (x));
+      split = find_split_point (&SET_SRC (x), insn);
       if (split && split != &SET_SRC (x))
        return split;
 
@@ -2121,7 +2431,7 @@ find_split_point (loc)
 
          SUBST (SET_DEST (x), dest);
 
-         split = find_split_point (&SET_SRC (x));
+         split = find_split_point (&SET_SRC (x), insn);
          if (split && split != &SET_SRC (x))
            return split;
        }
@@ -2132,6 +2442,33 @@ find_split_point (loc)
 
       switch (code)
        {
+       case AND:
+         /* If we are AND'ing with a large constant that is only a single
+            bit and the result is only being used in a context where we
+            need to know if it is zero or non-zero, replace it with a bit
+            extraction.  This will avoid the large constant, which might
+            have taken more than one insn to make.  If the constant were
+            not a valid argument to the AND but took only one insn to make,
+            this is no worse, but if it took more than one insn, it will
+            be better.  */
+
+         if (GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT
+             && GET_CODE (XEXP (SET_SRC (x), 0)) == REG
+             && (pos = exact_log2 (INTVAL (XEXP (SET_SRC (x), 1)))) >= 7
+             && GET_CODE (SET_DEST (x)) == REG
+             && (split = find_single_use (SET_DEST (x), insn, NULL_PTR)) != 0
+             && (GET_CODE (*split) == EQ || GET_CODE (*split) == NE)
+             && XEXP (*split, 0) == SET_DEST (x)
+             && XEXP (*split, 1) == const0_rtx)
+           {
+             SUBST (SET_SRC (x),
+                    make_extraction (GET_MODE (SET_DEST (x)),
+                                     XEXP (SET_SRC (x), 0),
+                                     pos, NULL_RTX, 1, 1, 0, 0));
+             return find_split_point (loc, insn);
+           }
+         break;
+
        case SIGN_EXTEND:
          inner = XEXP (SET_SRC (x), 0);
          pos = 0;
@@ -2160,7 +2497,13 @@ find_split_point (loc)
        {
          enum machine_mode mode = GET_MODE (SET_SRC (x));
 
-         if (unsignedp && len < HOST_BITS_PER_WIDE_INT)
+         /* For unsigned, we have a choice of a shift followed by an
+            AND or two shifts.  Use two shifts for field sizes where the
+            constant might be too large.  We assume here that we can
+            always at least get 8-bit constants in an AND insn, which is
+            true for every current RISC.  */
+
+         if (unsignedp && len <= 8)
            {
              SUBST (SET_SRC (x),
                     gen_rtx_combine
@@ -2170,7 +2513,7 @@ find_split_point (loc)
                                       GEN_INT (pos)),
                      GEN_INT (((HOST_WIDE_INT) 1 << len) - 1)));
 
-             split = find_split_point (&SET_SRC (x));
+             split = find_split_point (&SET_SRC (x), insn);
              if (split && split != &SET_SRC (x))
                return split;
            }
@@ -2178,14 +2521,14 @@ find_split_point (loc)
            {
              SUBST (SET_SRC (x),
                     gen_rtx_combine
-                    (ASHIFTRT, mode,
+                    (unsignedp ? LSHIFTRT : ASHIFTRT, mode,
                      gen_rtx_combine (ASHIFT, mode,
                                       gen_lowpart_for_combine (mode, inner),
                                       GEN_INT (GET_MODE_BITSIZE (mode)
                                                - len - pos)),
                      GEN_INT (GET_MODE_BITSIZE (mode) - len)));
 
-             split = find_split_point (&SET_SRC (x));
+             split = find_split_point (&SET_SRC (x), insn);
              if (split && split != &SET_SRC (x))
                return split;
            }
@@ -2231,7 +2574,7 @@ find_split_point (loc)
                                                   GET_MODE (x),
                                                   XEXP (XEXP (x, 0), 0),
                                                   XEXP (XEXP (x, 1), 0))));
-         return find_split_point (loc);
+         return find_split_point (loc, insn);
        }
 
       /* Many RISC machines have a large set of logical insns.  If the
@@ -2251,14 +2594,14 @@ find_split_point (loc)
     {
     case 'b':                  /* This is ZERO_EXTRACT and SIGN_EXTRACT.  */
     case '3':
-      split = find_split_point (&XEXP (x, 2));
+      split = find_split_point (&XEXP (x, 2), insn);
       if (split)
        return split;
       /* ... fall through ... */
     case '2':
     case 'c':
     case '<':
-      split = find_split_point (&XEXP (x, 1));
+      split = find_split_point (&XEXP (x, 1), insn);
       if (split)
        return split;
       /* ... fall through ... */
@@ -2268,7 +2611,7 @@ find_split_point (loc)
       if (GET_CODE (x) != AND && GET_CODE (XEXP (x, 0)) == AND)
        return &XEXP (x, 0);
 
-      split = find_split_point (&XEXP (x, 0));
+      split = find_split_point (&XEXP (x, 0), insn);
       if (split)
        return split;
       return loc;
@@ -2446,6 +2789,26 @@ subst (x, from, to, in_dest, unique_copy)
        }
     }
 
+  /* We come back to here if we have replaced the expression with one of
+     a different code and it is likely that further simplification will be
+     possible.  */
+
+ restart:
+
+  /* If we have restarted more than 4 times, we are probably looping, so
+     give up.  */
+  if (++n_restarts > 4)
+    return x;
+
+  /* If we are restarting at all, it means that we no longer know the
+     original mode of operand 0 (since we have probably changed the
+     form of X).  */
+
+  if (n_restarts > 1)
+    op0_mode = VOIDmode;
+
+  code = GET_CODE (x);
+
   /* If this is a commutative operation, put a constant last and a complex
      expression first.  We don't need to do this for comparisons here.  */
   if (GET_RTX_CLASS (code) == 'c'
@@ -2461,6 +2824,83 @@ subst (x, from, to, in_dest, unique_copy)
       SUBST (XEXP (x, 1), temp);
     }
 
+  /* If this is a PLUS, MINUS, or MULT, and the first operand is the
+     sign extension of a PLUS with a constant, reverse the order of the sign
+     extension and the addition. Note that this not the same as the original
+     code, but overflow is undefined for signed values.  Also note that the
+     PLUS will have been partially moved "inside" the sign-extension, so that
+     the first operand of X will really look like:
+         (ashiftrt (plus (ashift A C4) C5) C4).
+     We convert this to
+         (plus (ashiftrt (ashift A C4) C2) C4)
+     and replace the first operand of X with that expression.  Later parts
+     of this function may simplify the expression further.
+
+     For example, if we start with (mult (sign_extend (plus A C1)) C2),
+     we swap the SIGN_EXTEND and PLUS.  Later code will apply the
+     distributive law to produce (plus (mult (sign_extend X) C1) C3).
+
+     We do this to simplify address expressions.  */
+
+  if ((code == PLUS || code == MINUS || code == MULT)
+      && GET_CODE (XEXP (x, 0)) == ASHIFTRT
+      && GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS
+      && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == ASHIFT
+      && GET_CODE (XEXP (XEXP (XEXP (XEXP (x, 0), 0), 0), 1)) == CONST_INT
+      && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+      && XEXP (XEXP (XEXP (XEXP (x, 0), 0), 0), 1) == XEXP (XEXP (x, 0), 1)
+      && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT
+      && (temp = simplify_binary_operation (ASHIFTRT, mode,
+                                           XEXP (XEXP (XEXP (x, 0), 0), 1),
+                                           XEXP (XEXP (x, 0), 1))) != 0)
+    {
+      rtx new
+       = simplify_shift_const (NULL_RTX, ASHIFT, mode,
+                               XEXP (XEXP (XEXP (XEXP (x, 0), 0), 0), 0),
+                               INTVAL (XEXP (XEXP (x, 0), 1)));
+
+      new = simplify_shift_const (NULL_RTX, ASHIFTRT, mode, new,
+                                 INTVAL (XEXP (XEXP (x, 0), 1)));
+
+      SUBST (XEXP (x, 0), gen_binary (PLUS, mode, new, temp));
+    }
+
+  /* If this is a simple operation applied to an IF_THEN_ELSE, try 
+     applying it to the arms of the IF_THEN_ELSE.  This often simplifies
+     things.  Don't deal with operations that change modes here.  */
+
+  if ((GET_RTX_CLASS (code) == '2' || GET_RTX_CLASS (code) == 'c')
+      && GET_CODE (XEXP (x, 0)) == IF_THEN_ELSE)
+    {
+      /* Don't do this by using SUBST inside X since we might be messing
+        up a shared expression.  */
+      rtx cond = XEXP (XEXP (x, 0), 0);
+      rtx t_arm = subst (gen_binary (code, mode, XEXP (XEXP (x, 0), 1),
+                                    XEXP (x, 1)),
+                        pc_rtx, pc_rtx, 0, 0);
+      rtx f_arm = subst (gen_binary (code, mode, XEXP (XEXP (x, 0), 2),
+                                    XEXP (x, 1)),
+                        pc_rtx, pc_rtx, 0, 0);
+
+
+      x = gen_rtx (IF_THEN_ELSE, mode, cond, t_arm, f_arm);
+      goto restart;
+    }
+
+  else if (GET_RTX_CLASS (code) == '1'
+          && GET_CODE (XEXP (x, 0)) == IF_THEN_ELSE
+          && GET_MODE (XEXP (x, 0)) == mode)
+    {
+      rtx cond = XEXP (XEXP (x, 0), 0);
+      rtx t_arm = subst (gen_unary (code, mode, XEXP (XEXP (x, 0), 1)),
+                        pc_rtx, pc_rtx, 0, 0);
+      rtx f_arm = subst (gen_unary (code, mode, XEXP (XEXP (x, 0), 2)),
+                        pc_rtx, pc_rtx, 0, 0);
+
+      x = gen_rtx_combine (IF_THEN_ELSE, mode, cond, t_arm, f_arm);
+      goto restart;
+    }
+
   /* Try to fold this expression in case we have constants that weren't
      present before.  */
   temp = 0;
@@ -2490,23 +2930,11 @@ subst (x, from, to, in_dest, unique_copy)
     }
 
   if (temp)
-    x = temp;
-
-  /* We come back to here if we have replaced the expression with one of
-     a different code and it is likely that further simplification will be
-     possible.  */
-
- restart:
-
-  /* If we have restarted more than 4 times, we are probably looping, so
-     give up.  */
-  if (++n_restarts > 4)
-    return x;
-
-  code = GET_CODE (x);
+    x = temp, code = GET_CODE (temp);
 
   /* First see if we can apply the inverse distributive law.  */
-  if (code == PLUS || code == MINUS || code == IOR || code == XOR)
+  if (code == PLUS || code == MINUS
+      || code == AND || code == IOR || code == XOR)
     {
       x = apply_distributive_law (x);
       code = GET_CODE (x);
@@ -2519,7 +2947,7 @@ subst (x, from, to, in_dest, unique_copy)
        || code == MULT || code == AND || code == IOR || code == XOR
        || code == DIV || code == UDIV
        || code == SMAX || code == SMIN || code == UMAX || code == UMIN)
-      && GET_MODE_CLASS (mode) == MODE_INT)
+      && INTEGRAL_MODE_P (mode))
     {
       if (GET_CODE (XEXP (x, 0)) == code)
        {
@@ -2625,10 +3053,16 @@ subst (x, from, to, in_dest, unique_copy)
 
       /* SUBREG of a hard register => just change the register number
         and/or mode.  If the hard register is not valid in that mode,
-        suppress this combination.  */
+        suppress this combination.  If the hard register is the stack,
+        frame, or argument pointer, leave this as a SUBREG.  */
 
       if (GET_CODE (SUBREG_REG (x)) == REG
-         && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER)
+         && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER
+         && REGNO (SUBREG_REG (x)) != FRAME_POINTER_REGNUM
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+         && REGNO (SUBREG_REG (x)) != ARG_POINTER_REGNUM
+#endif
+         && REGNO (SUBREG_REG (x)) != STACK_POINTER_REGNUM)
        {
          if (HARD_REGNO_MODE_OK (REGNO (SUBREG_REG (x)) + SUBREG_WORD (x),
                                  mode))
@@ -2654,30 +3088,26 @@ subst (x, from, to, in_dest, unique_copy)
            return temp;
        }
        
+      /* If we want a subreg of a constant, at offset 0,
+        take the low bits.  On a little-endian machine, that's
+        always valid.  On a big-endian machine, it's valid
+        only if the constant's mode fits in one word.  */
       if (CONSTANT_P (SUBREG_REG (x)) && subreg_lowpart_p (x)
-         && GET_MODE_SIZE (mode) < GET_MODE_SIZE (op0_mode))
+         && GET_MODE_SIZE (mode) < GET_MODE_SIZE (op0_mode)
+#if WORDS_BIG_ENDIAN
+         && GET_MODE_BITSIZE (op0_mode) <= BITS_PER_WORD
+#endif
+         )
        return gen_lowpart_for_combine (mode, SUBREG_REG (x));
 
       /* If we are narrowing the object, we need to see if we can simplify
         the expression for the object knowing that we only need the
-        low-order bits.  We do this by computing an AND of the object
-        with only the bits we care about.  That will produce any needed
-        simplifications.  If the resulting computation is just the
-        AND with the significant bits, our operand is the first operand
-        of the AND.  Otherwise, it is the resulting expression.  */
+        low-order bits.  */
+
       if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
-         && subreg_lowpart_p (x)
-         && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x)))
-             <= HOST_BITS_PER_WIDE_INT))
-       {
-         temp = simplify_and_const_int (NULL_RTX, GET_MODE (SUBREG_REG (x)),
-                                        SUBREG_REG (x), GET_MODE_MASK (mode));
-         if (GET_CODE (temp) == AND && GET_CODE (XEXP (temp, 1)) == CONST_INT
-             && INTVAL (XEXP (temp, 1)) == GET_MODE_MASK (mode))
-           temp = XEXP (temp, 0);
-         return gen_lowpart_for_combine (mode, temp);
-       }
-       
+         && subreg_lowpart_p (x))
+       return force_to_mode (SUBREG_REG (x), mode, GET_MODE_BITSIZE (mode),
+                             NULL_RTX);
       break;
 
     case NOT:
@@ -2696,6 +3126,17 @@ subst (x, from, to, in_dest, unique_copy)
          goto restart;
        }
 
+      /* (not (xor X C)) for C constant is (xor X D) with D = ~ C.  */
+      if (GET_CODE (XEXP (x, 0)) == XOR
+         && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+         && (temp = simplify_unary_operation (NOT, mode,
+                                              XEXP (XEXP (x, 0), 1),
+                                              mode)) != 0)
+       {
+         SUBST (XEXP (XEXP (x, 0), 1), temp);
+         return XEXP (x, 0);
+       }
+             
       /* (not (ashift 1 X)) is (rotate ~1 X).  We used to do this for operands
         other than 1, but that is not valid.  We could do a similar
         simplification for (not (lshiftrt C X)) where C is just the sign bit,
@@ -2732,6 +3173,16 @@ subst (x, from, to, in_dest, unique_copy)
        return gen_rtx_combine (reverse_condition (GET_CODE (XEXP (x, 0))),
                                mode, XEXP (XEXP (x, 0), 0),
                                XEXP (XEXP (x, 0), 1));
+
+      /* (ashiftrt foo C) where C is the number of bits in FOO minus 1
+        is (lt foo (const_int 0)), so we can perform the above
+        simplification.  */
+
+      if (XEXP (x, 1) == const1_rtx
+         && GET_CODE (XEXP (x, 0)) == ASHIFTRT
+         && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+         && INTVAL (XEXP (XEXP (x, 0), 1)) == GET_MODE_BITSIZE (mode) - 1)
+       return gen_rtx_combine (GE, mode, XEXP (XEXP (x, 0), 0), const0_rtx);
 #endif
 
       /* Apply De Morgan's laws to reduce number of patterns for machines
@@ -2784,26 +3235,9 @@ subst (x, from, to, in_dest, unique_copy)
          goto restart;
        }
 
-      /* (neg (abs X)) is X if X is a value known to be either -1 or 0.  */
-      if (GET_CODE (XEXP (x, 0)) == ABS
-         && ((GET_CODE (XEXP (XEXP (x, 0), 0)) == SIGN_EXTRACT
-              && XEXP (XEXP (XEXP (x, 0), 0), 1) == const1_rtx)
-             || (GET_CODE (XEXP (XEXP (x, 0), 0)) == ASHIFTRT
-                 && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT
-                 && (INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1))
-                     == GET_MODE_BITSIZE (mode) - 1))
-             || ((temp = get_last_value (XEXP (XEXP (x, 0), 0))) != 0
-                 && ((GET_CODE (temp) == SIGN_EXTRACT
-                      && XEXP (temp, 1) == const1_rtx)
-                     || (GET_CODE (temp) == ASHIFTRT
-                         && GET_CODE (XEXP (temp, 1)) == CONST_INT
-                         && (INTVAL (XEXP (temp, 1))
-                             == GET_MODE_BITSIZE (mode) - 1))))))
-       return XEXP (XEXP (x, 0), 0);
-
       /* (neg (minus X Y)) can become (minus Y X).  */
       if (GET_CODE (XEXP (x, 0)) == MINUS
-         && (GET_MODE_CLASS (mode) != MODE_FLOAT
+         && (! FLOAT_MODE_P (mode)
              /* x-y != -(y-x) with IEEE floating point. */
              || TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT))
        {
@@ -2812,6 +3246,14 @@ subst (x, from, to, in_dest, unique_copy)
          goto restart;
        }
 
+      /* (neg (xor A 1)) is (plus A -1) if A is known to be either 0 or 1. */
+      if (GET_CODE (XEXP (x, 0)) == XOR && XEXP (XEXP (x, 0), 1) == const1_rtx
+         && nonzero_bits (XEXP (XEXP (x, 0), 0), mode) == 1)
+       {
+         x = gen_binary (PLUS, mode, XEXP (XEXP (x, 0), 0), constm1_rtx);
+         goto restart;
+       }
+
       /* NEG commutes with ASHIFT since it is multiplication.  Only do this
         if we can then eliminate the NEG (e.g.,
         if the operand is a constant).  */
@@ -2842,7 +3284,7 @@ subst (x, from, to, in_dest, unique_copy)
          goto restart;
        }
 
-      /* If X has only a single bit significant, say, bit I, convert
+      /* If X has only a single bit that might be nonzero, say, bit I, convert
         (neg X) to (ashiftrt (ashift X C-I) C-I) where C is the bitsize of
         MODE minus 1.  This will convert (neg (zero_extract X 1 Y)) to
         (sign_extract X 1 Y).  But only do this if TEMP isn't a register
@@ -2852,7 +3294,7 @@ subst (x, from, to, in_dest, unique_copy)
       if (GET_CODE (temp) != REG
          && ! (GET_CODE (temp) == SUBREG
                && GET_CODE (SUBREG_REG (temp)) == REG)
-         && (i = exact_log2 (significant_bits (temp, mode))) >= 0)
+         && (i = exact_log2 (nonzero_bits (temp, mode))) >= 0)
        {
          rtx temp1 = simplify_shift_const
            (NULL_RTX, ASHIFTRT, mode,
@@ -2890,7 +3332,7 @@ subst (x, from, to, in_dest, unique_copy)
 
       /* In IEEE floating point, x-0 is not the same as x.  */
       if ((TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
-          || GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) == MODE_INT)
+          || ! FLOAT_MODE_P (GET_MODE (XEXP (x, 0))))
          && XEXP (x, 1) == CONST0_RTX (GET_MODE (XEXP (x, 0))))
        return XEXP (x, 0);
       break;
@@ -2954,7 +3396,22 @@ subst (x, from, to, in_dest, unique_copy)
          goto restart;
        }
 
-      /* If only the low-order bit of X is significant, (plus x -1)
+      /* (plus (comparison A B) C) can become (neg (rev-comp A B)) if
+        C is 1 and STORE_FLAG_VALUE is -1 or if C is -1 and STORE_FLAG_VALUE
+        is 1.  This produces better code than the alternative immediately
+        below.  */
+      if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
+         && reversible_comparison_p (XEXP (x, 0))
+         && ((STORE_FLAG_VALUE == -1 && XEXP (x, 1) == const1_rtx)
+             || (STORE_FLAG_VALUE == 1 && XEXP (x, 1) == constm1_rtx)))
+       {
+         x = gen_binary (reverse_condition (GET_CODE (XEXP (x, 0))),
+                         mode, XEXP (XEXP (x, 0), 0), XEXP (XEXP (x, 0), 1));
+         x = gen_unary (NEG, mode, x);
+         goto restart;
+       }
+
+      /* If only the low-order bit of X is possibly nonzero, (plus x -1)
         can become (ashiftrt (ashift (xor x 1) C) C) where C is
         the bitsize of the mode - 1.  This allows simplification of
         "a = (b & 8) == 0;"  */
@@ -2962,7 +3419,7 @@ subst (x, from, to, in_dest, unique_copy)
          && GET_CODE (XEXP (x, 0)) != REG
          && ! (GET_CODE (XEXP (x,0)) == SUBREG
                && GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG)
-         && significant_bits (XEXP (x, 0), mode) == 1)
+         && nonzero_bits (XEXP (x, 0), mode) == 1)
        {
          x = simplify_shift_const
            (NULL_RTX, ASHIFTRT, mode,
@@ -2979,8 +3436,9 @@ subst (x, from, to, in_dest, unique_copy)
         for example in cases like ((a & 1) + (a & 2)), which can
         become a & 3.  */
 
-      if ((significant_bits (XEXP (x, 0), mode)
-          & significant_bits (XEXP (x, 1), mode)) == 0)
+      if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+         && (nonzero_bits (XEXP (x, 0), mode)
+             & nonzero_bits (XEXP (x, 1), mode)) == 0)
        {
          x = gen_binary (IOR, mode, XEXP (x, 0), XEXP (x, 1));
          goto restart;
@@ -3081,30 +3539,41 @@ subst (x, from, to, in_dest, unique_copy)
 
 #if STORE_FLAG_VALUE == 1
          /* If STORE_FLAG_VALUE is 1, we can convert (ne x 0) to simply X
-            if only the low-order bit is significant in X (such as when
+            if only the low-order bit is possibly nonzero in X (such as when
             X is a ZERO_EXTRACT of one bit.  Similarly, we can convert
-            EQ to (xor X 1).  */
+            EQ to (xor X 1).  Remove any ZERO_EXTRACT we made when thinking
+            this was a comparison.  It may now be simpler to use, e.g., an
+            AND.  If a ZERO_EXTRACT is indeed appropriate, it will
+            be placed back by the call to make_compound_operation in the
+            SET case.  */
          if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
              && op1 == const0_rtx
-             && significant_bits (op0, GET_MODE (op0)) == 1)
-           return gen_lowpart_for_combine (mode, op0);
+             && nonzero_bits (op0, GET_MODE (op0)) == 1)
+           return gen_lowpart_for_combine (mode,
+                                           expand_compound_operation (op0));
          else if (new_code == EQ && GET_MODE_CLASS (mode) == MODE_INT
                   && op1 == const0_rtx
-                  && significant_bits (op0, GET_MODE (op0)) == 1)
-           return gen_rtx_combine (XOR, mode,
-                                   gen_lowpart_for_combine (mode, op0),
-                                   const1_rtx);
+                  && nonzero_bits (op0, GET_MODE (op0)) == 1)
+           {
+             op0 = expand_compound_operation (op0);
+
+             x = gen_rtx_combine (XOR, mode,
+                                  gen_lowpart_for_combine (mode, op0),
+                                  const1_rtx);
+             goto restart;
+           }
 #endif
 
 #if STORE_FLAG_VALUE == -1
          /* If STORE_FLAG_VALUE is -1, we can convert (ne x 0)
-            to (neg x) if only the low-order bit of X is significant.
+            to (neg x) if only the low-order bit of X can be nonzero.
             This converts (ne (zero_extract X 1 Y) 0) to
             (sign_extract X 1 Y).  */
          if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
              && op1 == const0_rtx
-             && significant_bits (op0, GET_MODE (op0)) == 1)
+             && nonzero_bits (op0, GET_MODE (op0)) == 1)
            {
+             op0 = expand_compound_operation (op0);
              x = gen_rtx_combine (NEG, mode,
                                   gen_lowpart_for_combine (mode, op0));
              goto restart;
@@ -3112,19 +3581,20 @@ subst (x, from, to, in_dest, unique_copy)
 #endif
 
          /* If STORE_FLAG_VALUE says to just test the sign bit and X has just
-            one significant bit, we can convert (ne x 0) to (ashift x c)
-            where C puts the bit in the sign bit.  Remove any AND with
-            STORE_FLAG_VALUE when we are done, since we are only going to
-            test the sign bit.  */
+            one bit that might be nonzero, we can convert (ne x 0) to
+            (ashift x c) where C puts the bit in the sign bit.  Remove any
+            AND with STORE_FLAG_VALUE when we are done, since we are only
+            going to test the sign bit.  */
          if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
              && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
              && (STORE_FLAG_VALUE
                  == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))
              && op1 == const0_rtx
              && mode == GET_MODE (op0)
-             && (i = exact_log2 (significant_bits (op0, GET_MODE (op0)))) >= 0)
+             && (i = exact_log2 (nonzero_bits (op0, GET_MODE (op0)))) >= 0)
            {
-             x = simplify_shift_const (NULL_RTX, ASHIFT, mode, op0,
+             x = simplify_shift_const (NULL_RTX, ASHIFT, mode,
+                                       expand_compound_operation (op0),
                                        GET_MODE_BITSIZE (mode) - 1 - i);
              if (GET_CODE (x) == AND && XEXP (x, 1) == const_true_rtx)
                return XEXP (x, 0);
@@ -3144,21 +3614,243 @@ subst (x, from, to, in_dest, unique_copy)
       break;
          
     case IF_THEN_ELSE:
+      /* Sometimes we can simplify the arm of an IF_THEN_ELSE if a register
+        used in it is being compared against certain values.  Get the
+        true and false comparisons and see if that says anything about the
+        value of each arm.  */
+
+      if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
+         && reversible_comparison_p (XEXP (x, 0))
+         && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG)
+       {
+         HOST_WIDE_INT nzb;
+         rtx from = XEXP (XEXP (x, 0), 0);
+         enum rtx_code true_code = GET_CODE (XEXP (x, 0));
+         enum rtx_code false_code = reverse_condition (true_code);
+         rtx true_val = XEXP (XEXP (x, 0), 1);
+         rtx false_val = true_val;
+         rtx true_arm = XEXP (x, 1);
+         rtx false_arm = XEXP (x, 2);
+         int swapped = 0;
+
+         /* If FALSE_CODE is EQ, swap the codes and arms.  */
+
+         if (false_code == EQ)
+           {
+             swapped = 1, true_code = EQ, false_code = NE;
+             true_arm = XEXP (x, 2), false_arm = XEXP (x, 1);
+           }
+
+         /* If we are comparing against zero and the expression being tested
+            has only a single bit that might be nonzero, that is its value
+            when it is not equal to zero.  Similarly if it is known to be
+            -1 or 0.  */
+
+         if (true_code == EQ && true_val == const0_rtx
+             && exact_log2 (nzb = nonzero_bits (from, GET_MODE (from))) >= 0)
+           false_code = EQ, false_val = GEN_INT (nzb);
+         else if (true_code == EQ && true_val == const0_rtx
+                  && (num_sign_bit_copies (from, GET_MODE (from))
+                      == GET_MODE_BITSIZE (GET_MODE (from))))
+           false_code = EQ, false_val = constm1_rtx;
+
+         /* Now simplify an arm if we know the value of the register
+            in the branch and it is used in the arm.  Be carefull due to
+            the potential of locally-shared RTL.  */
+
+         if (reg_mentioned_p (from, true_arm))
+           true_arm = subst (known_cond (copy_rtx (true_arm), true_code,
+                                         from, true_val),
+                             pc_rtx, pc_rtx, 0, 0);
+         if (reg_mentioned_p (from, false_arm))
+           false_arm = subst (known_cond (copy_rtx (false_arm), false_code,
+                                          from, false_val),
+                              pc_rtx, pc_rtx, 0, 0);
+
+         SUBST (XEXP (x, 1), swapped ? false_arm : true_arm);
+         SUBST (XEXP (x, 2), swapped ? true_arm : false_arm);
+       }
+      
       /* If we have (if_then_else FOO (pc) (label_ref BAR)) and FOO can be
         reversed, do so to avoid needing two sets of patterns for
-        subtract-and-branch insns.  */
-      if (XEXP (x, 1) == pc_rtx
-         && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
-         && reversible_comparison_p (XEXP (x, 0)))
+        subtract-and-branch insns.  Similarly if we have a constant in that
+        position or if the third operand is the same as the first operand
+        of the comparison.  */
+
+      if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
+         && reversible_comparison_p (XEXP (x, 0))
+         && (XEXP (x, 1) == pc_rtx || GET_CODE (XEXP (x, 1)) == CONST_INT
+             || rtx_equal_p (XEXP (x, 2), XEXP (XEXP (x, 0), 0))))
        {
          SUBST (XEXP (x, 0),
-                gen_rtx_combine (reverse_condition (GET_CODE (XEXP (x, 0))),
-                                 GET_MODE (XEXP (x, 0)),
-                                 XEXP (XEXP (x, 0), 0),
-                                 XEXP (XEXP (x, 0), 1)));
+                gen_binary (reverse_condition (GET_CODE (XEXP (x, 0))),
+                            GET_MODE (XEXP (x, 0)),
+                            XEXP (XEXP (x, 0), 0), XEXP (XEXP (x, 0), 1)));
+
+         temp = XEXP (x, 1);
          SUBST (XEXP (x, 1), XEXP (x, 2));
-         SUBST (XEXP (x, 2), pc_rtx);
+         SUBST (XEXP (x, 2), temp);
        }
+
+      /* If the two arms are identical, we don't need the comparison.  */
+
+      if (rtx_equal_p (XEXP (x, 1), XEXP (x, 2))
+         && ! side_effects_p (XEXP (x, 0)))
+       return XEXP (x, 1);
+
+      /* Look for cases where we have (abs x) or (neg (abs X)).  */
+
+      if (GET_MODE_CLASS (mode) == MODE_INT
+         && GET_CODE (XEXP (x, 2)) == NEG
+         && rtx_equal_p (XEXP (x, 1), XEXP (XEXP (x, 2), 0))
+         && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
+         && rtx_equal_p (XEXP (x, 1), XEXP (XEXP (x, 0), 0))
+         && ! side_effects_p (XEXP (x, 1)))
+       switch (GET_CODE (XEXP (x, 0)))
+         {
+         case GT:
+         case GE:
+           x = gen_unary (ABS, mode, XEXP (x, 1));
+           goto restart;
+         case LT:
+         case LE:
+           x = gen_unary (NEG, mode, gen_unary (ABS, mode, XEXP (x, 1)));
+           goto restart;
+         }
+
+      /* Look for MIN or MAX.  */
+
+      if (! FLOAT_MODE_P (mode)
+         && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
+         && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1))
+         && rtx_equal_p (XEXP (XEXP (x, 0), 1), XEXP (x, 2))
+         && ! side_effects_p (XEXP (x, 0)))
+       switch (GET_CODE (XEXP (x, 0)))
+         {
+         case GE:
+         case GT:
+           x = gen_binary (SMAX, mode, XEXP (x, 1), XEXP (x, 2));
+           goto restart;
+         case LE:
+         case LT:
+           x = gen_binary (SMIN, mode, XEXP (x, 1), XEXP (x, 2));
+           goto restart;
+         case GEU:
+         case GTU:
+           x = gen_binary (UMAX, mode, XEXP (x, 1), XEXP (x, 2));
+           goto restart;
+         case LEU:
+         case LTU:
+           x = gen_binary (UMIN, mode, XEXP (x, 1), XEXP (x, 2));
+           goto restart;
+         }
+
+      /* If we have something like (if_then_else (ne A 0) (OP X C) X),
+        A is known to be either 0 or 1, and OP is an identity when its
+        second operand is zero, this can be done as (OP X (mult A C)).
+        Similarly if A is known to be 0 or -1 and also similarly if we have
+        a ZERO_EXTEND or SIGN_EXTEND as long as X is already extended (so
+        we don't destroy it).  */
+
+      if (mode != VOIDmode
+         && (GET_CODE (XEXP (x, 0)) == EQ || GET_CODE (XEXP (x, 0)) == NE)
+         && XEXP (XEXP (x, 0), 1) == const0_rtx
+         && (nonzero_bits (XEXP (XEXP (x, 0), 0), mode) == 1
+             || (num_sign_bit_copies (XEXP (XEXP (x, 0), 0), mode)
+                 == GET_MODE_BITSIZE (mode))))
+       {
+         rtx nz = make_compound_operation (GET_CODE (XEXP (x, 0)) == NE
+                                           ? XEXP (x, 1) : XEXP (x, 2));
+         rtx z = GET_CODE (XEXP (x, 0)) == NE ? XEXP (x, 2) : XEXP (x, 1);
+         rtx dir = (nonzero_bits (XEXP (XEXP (x, 0), 0), mode) == 1
+                    ? const1_rtx : constm1_rtx);
+         rtx c = 0;
+         enum machine_mode m = mode;
+         enum rtx_code op, extend_op = 0;
+
+         if ((GET_CODE (nz) == PLUS || GET_CODE (nz) == MINUS
+              || GET_CODE (nz) == IOR || GET_CODE (nz) == XOR
+              || GET_CODE (nz) == ASHIFT
+              || GET_CODE (nz) == LSHIFTRT || GET_CODE (nz) == ASHIFTRT)
+             && rtx_equal_p (XEXP (nz, 0), z))
+           c = XEXP (nz, 1), op = GET_CODE (nz);
+         else if (GET_CODE (nz) == SIGN_EXTEND
+                  && (GET_CODE (XEXP (nz, 0)) == PLUS
+                      || GET_CODE (XEXP (nz, 0)) == MINUS
+                      || GET_CODE (XEXP (nz, 0)) == IOR
+                      || GET_CODE (XEXP (nz, 0)) == XOR
+                      || GET_CODE (XEXP (nz, 0)) == ASHIFT
+                      || GET_CODE (XEXP (nz, 0)) == LSHIFTRT
+                      || GET_CODE (XEXP (nz, 0)) == ASHIFTRT)
+                  && GET_CODE (XEXP (XEXP (nz, 0), 0)) == SUBREG
+                  && subreg_lowpart_p (XEXP (XEXP (nz, 0), 0))
+                  && rtx_equal_p (SUBREG_REG (XEXP (XEXP (nz, 0), 0)), z)
+                  && (num_sign_bit_copies (z, GET_MODE (z))
+                      >= (GET_MODE_BITSIZE (mode)
+                          - GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (nz, 0), 0))))))
+           {
+             c = XEXP (XEXP (nz, 0), 1);
+             op = GET_CODE (XEXP (nz, 0));
+             extend_op = SIGN_EXTEND;
+             m = GET_MODE (XEXP (nz, 0));
+           }
+         else if (GET_CODE (nz) == ZERO_EXTEND
+                  && (GET_CODE (XEXP (nz, 0)) == PLUS
+                      || GET_CODE (XEXP (nz, 0)) == MINUS
+                      || GET_CODE (XEXP (nz, 0)) == IOR
+                      || GET_CODE (XEXP (nz, 0)) == XOR
+                      || GET_CODE (XEXP (nz, 0)) == ASHIFT
+                      || GET_CODE (XEXP (nz, 0)) == LSHIFTRT
+                      || GET_CODE (XEXP (nz, 0)) == ASHIFTRT)
+                  && GET_CODE (XEXP (XEXP (nz, 0), 0)) == SUBREG
+                  && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+                  && subreg_lowpart_p (XEXP (XEXP (nz, 0), 0))
+                  && rtx_equal_p (SUBREG_REG (XEXP (XEXP (nz, 0), 0)), z)
+                  && ((nonzero_bits (z, GET_MODE (z))
+                       & ~ GET_MODE_MASK (GET_MODE (XEXP (XEXP (nz, 0), 0))))
+                      == 0))
+           {
+             c = XEXP (XEXP (nz, 0), 1);
+             op = GET_CODE (XEXP (nz, 0));
+             extend_op = ZERO_EXTEND;
+             m = GET_MODE (XEXP (nz, 0));
+           }
+
+         if (c && ! side_effects_p (c) && ! side_effects_p (z))
+           {
+             temp
+               = gen_binary (MULT, m,
+                             gen_lowpart_for_combine (m,
+                                                      XEXP (XEXP (x, 0), 0)),
+                             gen_binary (MULT, m, c, dir));
+
+             temp = gen_binary (op, m, gen_lowpart_for_combine (m, z), temp);
+
+             if (extend_op != 0)
+               temp = gen_unary (extend_op, mode, temp);
+
+             return temp;
+           }
+       }
+
+      /* If we have (if_then_else (ne A 0) C1 0) and either A is known to 
+        be 0 or 1 and C1 is a single bit or A is known to be 0 or -1 and
+        C1 is the negation of a single bit, we can convert this operation
+        to a shift.  We can actually do this in more general cases, but it
+        doesn't seem worth it.  */
+
+      if (GET_CODE (XEXP (x, 0)) == NE && XEXP (XEXP (x, 0), 1) == const0_rtx
+         && XEXP (x, 2) == const0_rtx && GET_CODE (XEXP (x, 1)) == CONST_INT
+         && ((1 == nonzero_bits (XEXP (XEXP (x, 0), 0), mode)
+              && (i = exact_log2 (INTVAL (XEXP (x, 1)))) >= 0)
+             || ((num_sign_bit_copies (XEXP (XEXP (x, 0), 0), mode)
+                  == GET_MODE_BITSIZE (mode))
+                 && (i = exact_log2 (- INTVAL (XEXP (x, 1)))) >= 0)))
+       return
+         simplify_shift_const (NULL_RTX, ASHIFT, mode,
+                               gen_lowpart_for_combine (mode,
+                                                        XEXP (XEXP (x, 0), 0)),
+                               i);
       break;
          
     case ZERO_EXTRACT:
@@ -3182,49 +3874,6 @@ subst (x, from, to, in_dest, unique_copy)
       /* Convert this into a field assignment operation, if possible.  */
       x = make_field_assignment (x);
 
-      /* If we have (set x (subreg:m1 (op:m2 ...) 0)) with OP being some
-        operation, and X being a REG or (subreg (reg)), we may be able to
-        convert this to (set (subreg:m2 x) (op)).
-
-        We can always do this if M1 is narrower than M2 because that
-        means that we only care about the low bits of the result.
-
-        However, on most machines (those with BYTE_LOADS_ZERO_EXTEND
-        not defined), we cannot perform a narrower operation that
-        requested since the high-order bits will be undefined.  On
-        machine where BYTE_LOADS_ZERO_EXTEND are defined, however, this
-        transformation is safe as long as M1 and M2 have the same number
-        of words.  */
-      if (GET_CODE (SET_SRC (x)) == SUBREG
-         && subreg_lowpart_p (SET_SRC (x))
-         && GET_RTX_CLASS (GET_CODE (SUBREG_REG (SET_SRC (x)))) != 'o'
-         && (((GET_MODE_SIZE (GET_MODE (SET_SRC (x))) + (UNITS_PER_WORD - 1))
-              / UNITS_PER_WORD)
-             == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_SRC (x))))
-                  + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD))
-#ifndef BYTE_LOADS_ZERO_EXTEND
-         && (GET_MODE_SIZE (GET_MODE (SET_SRC (x)))
-             < GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_SRC (x)))))
-#endif
-         && (GET_CODE (SET_DEST (x)) == REG
-             || (GET_CODE (SET_DEST (x)) == SUBREG
-                 && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG)))
-       {
-         /* Get the object that will be the SUBREG_REG of the
-            SUBREG we are making.  Note that SUBREG_WORD will always
-            be zero because this will either be a paradoxical SUBREG
-            or a SUBREG with the same number of words on the outside and
-            inside.  */
-         rtx object = (GET_CODE (SET_DEST (x)) == REG ? SET_DEST (x)
-                       : SUBREG_REG (SET_DEST (x)));
-
-         SUBST (SET_DEST (x),
-                gen_rtx (SUBREG, GET_MODE (SUBREG_REG (SET_SRC (x))),
-                         object, 0));
-         SUBST (SET_SRC (x), SUBREG_REG (SET_SRC (x)));
-       }
-
       /* If we are setting CC0 or if the source is a COMPARE, look for the
         use of the comparison result and try to simplify it unless we already
         have used undobuf.other_insn.  */
@@ -3253,11 +3902,13 @@ subst (x, from, to, in_dest, unique_copy)
          /* Simplify our comparison, if possible.  */
          new_code = simplify_comparison (old_code, &op0, &op1);
 
-#if !defined (HAVE_cc0) && defined (EXTRA_CC_MODES)
+#ifdef EXTRA_CC_MODES
          /* If this machine has CC modes other than CCmode, check to see
             if we need to use a different CC mode here.  */
          compare_mode = SELECT_CC_MODE (new_code, op0, op1);
+#endif /* EXTRA_CC_MODES */
 
+#if !defined (HAVE_cc0) && defined (EXTRA_CC_MODES)
          /* If the mode changed, we have to change SET_DEST, the mode
             in the compare, and the mode in the place SET_DEST is used.
             If SET_DEST is a hard register, just build new versions with
@@ -3287,13 +3938,13 @@ subst (x, from, to, in_dest, unique_copy)
             in undobuf.other_insn.  */
          if (new_code != old_code)
            {
-             unsigned mask;
+             unsigned HOST_WIDE_INT mask;
 
              SUBST (*cc_use, gen_rtx_combine (new_code, GET_MODE (*cc_use),
                                               SET_DEST (x), const0_rtx));
 
              /* If the only change we made was to change an EQ into an
-                NE or vice versa, OP0 has only one significant bit,
+                NE or vice versa, OP0 has only one bit that might be nonzero,
                 and OP1 is zero, check if changing the user of the condition
                 code will produce a valid insn.  If it won't, we can keep
                 the original code in that insn by surrounding our operation
@@ -3304,13 +3955,12 @@ subst (x, from, to, in_dest, unique_copy)
                  && ! other_changed && op1 == const0_rtx
                  && (GET_MODE_BITSIZE (GET_MODE (op0))
                      <= HOST_BITS_PER_WIDE_INT)
-                 && (exact_log2 (mask = significant_bits (op0,
-                                                          GET_MODE (op0)))
+                 && (exact_log2 (mask = nonzero_bits (op0, GET_MODE (op0)))
                      >= 0))
                {
                  rtx pat = PATTERN (other_insn), note = 0;
 
-                 if ((recog_for_combine (&pat, undobuf.other_insn, &note) < 0
+                 if ((recog_for_combine (&pat, other_insn, &note) < 0
                       && ! check_asm_operands (pat)))
                    {
                      PUT_CODE (*cc_use, old_code);
@@ -3356,22 +4006,109 @@ subst (x, from, to, in_dest, unique_copy)
          SUBST (SET_SRC (x), temp);
        }
 
-#ifdef BYTE_LOADS_ZERO_EXTEND
+      /* If we have (set x (subreg:m1 (op:m2 ...) 0)) with OP being some
+        operation, and X being a REG or (subreg (reg)), we may be able to
+        convert this to (set (subreg:m2 x) (op)).
+
+        We can always do this if M1 is narrower than M2 because that
+        means that we only care about the low bits of the result.
+
+        However, on most machines (those with neither BYTE_LOADS_ZERO_EXTEND
+        nor BYTE_LOADS_SIGN_EXTEND defined), we cannot perform a
+        narrower operation that requested since the high-order bits will
+        be undefined.  On machine where BYTE_LOADS_*_EXTEND is defined,
+        however, this transformation is safe as long as M1 and M2 have
+        the same number of words.  */
+      if (GET_CODE (SET_SRC (x)) == SUBREG
+         && subreg_lowpart_p (SET_SRC (x))
+         && GET_RTX_CLASS (GET_CODE (SUBREG_REG (SET_SRC (x)))) != 'o'
+         && (((GET_MODE_SIZE (GET_MODE (SET_SRC (x))) + (UNITS_PER_WORD - 1))
+              / UNITS_PER_WORD)
+             == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_SRC (x))))
+                  + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD))
+#ifndef BYTE_LOADS_EXTEND
+         && (GET_MODE_SIZE (GET_MODE (SET_SRC (x)))
+             < GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_SRC (x)))))
+#endif
+         && (GET_CODE (SET_DEST (x)) == REG
+             || (GET_CODE (SET_DEST (x)) == SUBREG
+                 && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG)))
+       {
+         SUBST (SET_DEST (x),
+                gen_lowpart_for_combine (GET_MODE (SUBREG_REG (SET_SRC (x))),
+                                         SET_DEST (x)));
+         SUBST (SET_SRC (x), SUBREG_REG (SET_SRC (x)));
+       }
+
+#ifdef BYTE_LOADS_EXTEND
       /* If we have (set FOO (subreg:M (mem:N BAR) 0)) with
         M wider than N, this would require a paradoxical subreg.
         Replace the subreg with a zero_extend to avoid the reload that
         would otherwise be required. */
+
       if (GET_CODE (SET_SRC (x)) == SUBREG
          && subreg_lowpart_p (SET_SRC (x))
          && SUBREG_WORD (SET_SRC (x)) == 0
          && (GET_MODE_SIZE (GET_MODE (SET_SRC (x)))
              > GET_MODE_SIZE (GET_MODE (SUBREG_REG (SET_SRC (x)))))
          && GET_CODE (SUBREG_REG (SET_SRC (x))) == MEM)
-       SUBST (SET_SRC (x), gen_rtx_combine (ZERO_EXTEND,
+       SUBST (SET_SRC (x), gen_rtx_combine (LOAD_EXTEND,
                                             GET_MODE (SET_SRC (x)),
                                             XEXP (SET_SRC (x), 0)));
 #endif
 
+#ifndef HAVE_conditional_move
+
+      /* If we don't have a conditional move, SET_SRC is an IF_THEN_ELSE,
+        and we are comparing an item known to be 0 or -1 against 0, use a
+        logical operation instead. Check for one of the arms being an IOR
+        of the other arm with some value.  We compute three terms to be
+        IOR'ed together.  In practice, at most two will be nonzero.  Then
+        we do the IOR's.  */
+
+      if (GET_CODE (SET_DEST (x)) != PC
+         && GET_CODE (SET_SRC (x)) == IF_THEN_ELSE
+         && (GET_CODE (XEXP (SET_SRC (x), 0)) == EQ
+             || GET_CODE (XEXP (SET_SRC (x), 0)) == NE)
+         && XEXP (XEXP (SET_SRC (x), 0), 1) == const0_rtx
+         && (num_sign_bit_copies (XEXP (XEXP (SET_SRC (x), 0), 0),
+                                  GET_MODE (XEXP (XEXP (SET_SRC (x), 0), 0)))
+             == GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (SET_SRC (x), 0), 0))))
+         && ! side_effects_p (SET_SRC (x)))
+       {
+         rtx true = (GET_CODE (XEXP (SET_SRC (x), 0)) == NE
+                     ? XEXP (SET_SRC (x), 1) : XEXP (SET_SRC (x), 2));
+         rtx false = (GET_CODE (XEXP (SET_SRC (x), 0)) == NE
+                      ? XEXP (SET_SRC (x), 2) : XEXP (SET_SRC (x), 1));
+         rtx term1 = const0_rtx, term2, term3;
+
+         if (GET_CODE (true) == IOR && rtx_equal_p (XEXP (true, 0), false))
+           term1 = false, true = XEXP (true, 1), false = const0_rtx;
+         else if (GET_CODE (true) == IOR
+                  && rtx_equal_p (XEXP (true, 1), false))
+           term1 = false, true = XEXP (true, 0), false = const0_rtx;
+         else if (GET_CODE (false) == IOR
+                  && rtx_equal_p (XEXP (false, 0), true))
+           term1 = true, false = XEXP (false, 1), true = const0_rtx;
+         else if (GET_CODE (false) == IOR
+                  && rtx_equal_p (XEXP (false, 1), true))
+           term1 = true, false = XEXP (false, 0), true = const0_rtx;
+
+         term2 = gen_binary (AND, GET_MODE (SET_SRC (x)),
+                             XEXP (XEXP (SET_SRC (x), 0), 0), true);
+         term3 = gen_binary (AND, GET_MODE (SET_SRC (x)),
+                             gen_unary (NOT, GET_MODE (SET_SRC (x)),
+                                        XEXP (XEXP (SET_SRC (x), 0), 0)),
+                             false);
+
+         SUBST (SET_SRC (x),
+                gen_binary (IOR, GET_MODE (SET_SRC (x)),
+                            gen_binary (IOR, GET_MODE (SET_SRC (x)),
+                                        term1, term2),
+                            term3));
+       }
+#endif
       break;
 
     case AND:
@@ -3447,7 +4184,34 @@ subst (x, from, to, in_dest, unique_copy)
          goto restart;
        }
 
-      /* In the follow group of tests (and those in case IOR below),
+      /* If we have (and A B) with A not an object but that is known to
+        be -1 or 0, this is equivalent to the expression
+        (if_then_else (ne A (const_int 0)) B (const_int 0))
+        We make this conversion because it may allow further
+        simplifications and then allow use of conditional move insns.
+        If the machine doesn't have condition moves, code in case SET
+        will convert the IF_THEN_ELSE back to the logical operation.
+        We build the IF_THEN_ELSE here in case further simplification
+        is possible (e.g., we can convert it to ABS).  */
+
+      if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) != 'o'
+         && ! (GET_CODE (XEXP (x, 0)) == SUBREG
+               && GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (x, 0)))) == 'o')
+         && (num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0)))
+             == GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))))
+       {
+         rtx op0 = XEXP (x, 0);
+         rtx op1 = const0_rtx;
+         enum rtx_code comp_code
+           = simplify_comparison (NE, &op0, &op1);
+
+         x =  gen_rtx_combine (IF_THEN_ELSE, mode,
+                               gen_binary (comp_code, VOIDmode, op0, op1),
+                               XEXP (x, 1), const0_rtx);
+         goto restart;
+       }
+
+      /* In the following group of tests (and those in case IOR below),
         we start with some combination of logical operations and apply
         the distributive law followed by the inverse distributive law.
         Most of the time, this results in no change.  However, if some of
@@ -3515,6 +4279,12 @@ subst (x, from, to, in_dest, unique_copy)
       break;
 
     case IOR:
+      /* (ior A C) is C if all bits of A that might be nonzero are on in C.  */
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+         && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+         && (nonzero_bits (XEXP (x, 0), mode) & ~ INTVAL (XEXP (x, 1))) == 0)
+       return XEXP (x, 1);
+
       /* Convert (A & B) | A to A.  */
       if (GET_CODE (XEXP (x, 0)) == AND
          && (rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1))
@@ -3595,8 +4365,11 @@ subst (x, from, to, in_dest, unique_copy)
            SUBST (XEXP (x, 1), XEXP (XEXP (x, 1), 0));
          }
        else if (num_negated == 1)
-         return gen_rtx_combine (NOT, mode,
-                         gen_rtx_combine (XOR, mode, in1, in2));
+         {
+           x =  gen_unary (NOT, mode,
+                           gen_binary (XOR, mode, in1, in2));
+           goto restart;
+         }
       }
 
       /* Convert (xor (and A B) B) to (and (not A) B).  The latter may
@@ -3632,6 +4405,16 @@ subst (x, from, to, in_dest, unique_copy)
        return gen_rtx_combine (reverse_condition (GET_CODE (XEXP (x, 0))),
                                mode, XEXP (XEXP (x, 0), 0),
                                XEXP (XEXP (x, 0), 1));
+
+      /* (lshiftrt foo C) where C is the number of bits in FOO minus 1
+        is (lt foo (const_int 0)), so we can perform the above
+        simplification.  */
+
+      if (XEXP (x, 1) == const1_rtx
+         && GET_CODE (XEXP (x, 0)) == LSHIFTRT
+         && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+         && INTVAL (XEXP (XEXP (x, 0), 1)) == GET_MODE_BITSIZE (mode) - 1)
+       return gen_rtx_combine (GE, mode, XEXP (XEXP (x, 0), 0), const0_rtx);
 #endif
 
       /* (xor (comparison foo bar) (const_int sign-bit))
@@ -3656,7 +4439,7 @@ subst (x, from, to, in_dest, unique_copy)
       if (GET_CODE (XEXP (x, 0)) == FFS || GET_CODE (XEXP (x, 0)) == ABS
          || ((GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))
               <= HOST_BITS_PER_WIDE_INT)
-             && ((significant_bits (XEXP (x, 0), GET_MODE (XEXP (x, 0)))
+             && ((nonzero_bits (XEXP (x, 0), GET_MODE (XEXP (x, 0)))
                   & ((HOST_WIDE_INT) 1
                      << (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - 1)))
                  == 0)))
@@ -3664,18 +4447,7 @@ subst (x, from, to, in_dest, unique_copy)
 
 
       /* If operand is known to be only -1 or 0, convert ABS to NEG.  */
-      if ((GET_CODE (XEXP (x, 0)) == SIGN_EXTRACT
-          && XEXP (XEXP (x, 0), 1) == const1_rtx)
-         || (GET_CODE (XEXP (x, 0)) == ASHIFTRT
-             && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
-             && INTVAL (XEXP (XEXP (x, 0), 1)) == GET_MODE_BITSIZE (mode) - 1)
-         || ((temp = get_last_value (XEXP (x, 0))) != 0
-             && ((GET_CODE (temp) == SIGN_EXTRACT
-                  && XEXP (temp, 1) == const1_rtx)
-                 || (GET_CODE (temp) == ASHIFTRT
-                     && GET_CODE (XEXP (temp, 1)) == CONST_INT
-                     && (INTVAL (XEXP (temp, 1))
-                         == GET_MODE_BITSIZE (mode) - 1)))))
+      if (num_sign_bit_copies (XEXP (x, 0), mode) == GET_MODE_BITSIZE (mode))
        {
          x = gen_rtx_combine (NEG, mode, XEXP (x, 0));
          goto restart;
@@ -3957,9 +4729,9 @@ expand_field_assignment (x)
   return x;
 }
 \f
-/* Return an RTX for a reference to LEN bits of INNER.  POS is the starting
-   bit position (counted from the LSB) if >= 0; otherwise POS_RTX represents
-   the starting bit position.
+/* Return an RTX for a reference to LEN bits of INNER.  If POS_RTX is nonzero,
+   it is an RTX that represents a variable starting position; otherwise,
+   POS is the (constant) starting bit position (counted from the LSB).
 
    INNER may be a USE.  This will occur when we started with a bitfield
    that went outside the boundary of the object in memory, which is
@@ -3992,6 +4764,9 @@ make_extraction (mode, inner, pos, pos_rtx, len,
      int unsignedp;
      int in_dest, in_compare;
 {
+  /* This mode describes the size of the storage area
+     to fetch the overall value from.  Within that, we
+     ignore the POS lowest bits, etc.  */
   enum machine_mode is_mode = GET_MODE (inner);
   enum machine_mode inner_mode;
   enum machine_mode wanted_mem_mode = byte_mode;
@@ -4000,19 +4775,30 @@ make_extraction (mode, inner, pos, pos_rtx, len,
   enum machine_mode tmode = mode_for_size (len, MODE_INT, 1);
   int spans_byte = 0;
   rtx new = 0;
+  rtx orig_pos_rtx = pos_rtx;
 
   /* Get some information about INNER and get the innermost object.  */
   if (GET_CODE (inner) == USE)
+    /* (use:SI (mem:QI foo)) stands for (mem:SI foo).  */
     /* We don't need to adjust the position because we set up the USE
        to pretend that it was a full-word object.  */
     spans_byte = 1, inner = XEXP (inner, 0);
   else if (GET_CODE (inner) == SUBREG && subreg_lowpart_p (inner))
-    inner = SUBREG_REG (inner);
+    {
+      /* If going from (subreg:SI (mem:QI ...)) to (mem:QI ...),
+        consider just the QI as the memory to extract from.
+        The subreg adds or removes high bits; its mode is
+        irrelevant to the meaning of this extraction,
+        since POS and LEN count from the lsb.  */
+      if (GET_CODE (SUBREG_REG (inner)) == MEM)
+       is_mode = GET_MODE (SUBREG_REG (inner));
+      inner = SUBREG_REG (inner);
+    }
 
   inner_mode = GET_MODE (inner);
 
   if (pos_rtx && GET_CODE (pos_rtx) == CONST_INT)
-    pos = INTVAL (pos_rtx);
+    pos = INTVAL (pos_rtx), pos_rtx = 0;
 
   /* See if this can be done without an extraction.  We never can if the
      width of the field is not the same as that of some integer mode. For
@@ -4029,11 +4815,12 @@ make_extraction (mode, inner, pos, pos_rtx, len,
 
   if (tmode != BLKmode
       && ! (spans_byte && inner_mode != tmode)
-      && ((pos == 0 && GET_CODE (inner) == REG
+      && ((pos_rtx == 0 && pos == 0 && GET_CODE (inner) != MEM
           && (! in_dest
-              || (movstrict_optab->handlers[(int) tmode].insn_code
-                  != CODE_FOR_nothing)))
-         || (GET_CODE (inner) == MEM && pos >= 0
+              || (GET_CODE (inner) == REG
+                  && (movstrict_optab->handlers[(int) tmode].insn_code
+                      != CODE_FOR_nothing))))
+         || (GET_CODE (inner) == MEM && pos_rtx == 0
              && (pos
                  % (STRICT_ALIGNMENT ? GET_MODE_ALIGNMENT (tmode)
                     : BITS_PER_UNIT)) == 0
@@ -4044,39 +4831,38 @@ make_extraction (mode, inner, pos, pos_rtx, len,
                  || (! mode_dependent_address_p (XEXP (inner, 0))
                      && ! MEM_VOLATILE_P (inner))))))
     {
-      int offset = pos / BITS_PER_UNIT;
-         
       /* If INNER is a MEM, make a new MEM that encompasses just the desired
         field.  If the original and current mode are the same, we need not
         adjust the offset.  Otherwise, we do if bytes big endian.  
 
         If INNER is not a MEM, get a piece consisting of the just the field
-        of interest (in this case INNER must be a REG and POS must be 0).  */
+        of interest (in this case POS must be 0).  */
 
       if (GET_CODE (inner) == MEM)
        {
-#if BYTES_BIG_ENDIAN
-         if (inner_mode != tmode)
-           offset = (GET_MODE_SIZE (inner_mode)
-                     - GET_MODE_SIZE (tmode) - offset);
-#endif
+         int offset;
+         /* POS counts from lsb, but make OFFSET count in memory order.  */
+         if (BYTES_BIG_ENDIAN)
+           offset = (GET_MODE_BITSIZE (is_mode) - len - pos) / BITS_PER_UNIT;
+         else
+           offset = pos / BITS_PER_UNIT;
 
          new = gen_rtx (MEM, tmode, plus_constant (XEXP (inner, 0), offset));
          RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (inner);
          MEM_VOLATILE_P (new) = MEM_VOLATILE_P (inner);
          MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (inner);
        }
-      else if (GET_MODE (inner) == REG)
+      else if (GET_CODE (inner) == REG)
        /* We can't call gen_lowpart_for_combine here since we always want
           a SUBREG and it would sometimes return a new hard register.  */
        new = gen_rtx (SUBREG, tmode, inner,
                       (WORDS_BIG_ENDIAN
-                       && GET_MODE_SIZE (is_mode) > UNITS_PER_WORD)
-                      ? ((GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (tmode)
-                          / UNITS_PER_WORD))
-                      : 0);
+                       && GET_MODE_SIZE (inner_mode) > UNITS_PER_WORD
+                       ? ((GET_MODE_SIZE (inner_mode) - GET_MODE_SIZE (tmode))
+                          / UNITS_PER_WORD)
+                       : 0));
       else
-       new = gen_lowpart_for_combine (tmode, inner);
+       new = force_to_mode (inner, tmode, len, NULL_RTX);
 
       /* If this extraction is going into the destination of a SET, 
         make a STRICT_LOW_PART unless we made a MEM.  */
@@ -4095,10 +4881,11 @@ make_extraction (mode, inner, pos, pos_rtx, len,
                                 mode, new));
     }
 
-  /* Unless this is in a COMPARE or we have a funny memory reference,
-     don't do anything with field extracts starting at the low-order
-     bit since they are simple AND operations.  */
-  if (pos == 0 && ! in_dest && ! in_compare && ! spans_byte)
+  /* Unless this is a COMPARE or we have a funny memory reference,
+     don't do anything with zero-extending field extracts starting at
+     the low-order bit since they are simple AND operations.  */
+  if (pos_rtx == 0 && pos == 0 && ! in_dest
+      && ! in_compare && ! spans_byte && unsignedp)
     return 0;
 
   /* Get the mode to use should INNER be a MEM, the mode for the position,
@@ -4151,7 +4938,7 @@ make_extraction (mode, inner, pos, pos_rtx, len,
 #if BITS_BIG_ENDIAN
   /* If position is constant, compute new position.  Otherwise, build
      subtraction.  */
-  if (pos >= 0)
+  if (pos_rtx == 0)
     pos = (MAX (GET_MODE_BITSIZE (is_mode), GET_MODE_BITSIZE (wanted_mem_mode))
           - len - pos);
   else
@@ -4179,12 +4966,6 @@ make_extraction (mode, inner, pos, pos_rtx, len,
         endian in both bits and bytes or little endian in bits and bytes.
         If it is mixed, we must adjust.  */
             
-#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN
-      if (! spans_byte && is_mode != wanted_mem_mode)
-       offset = (GET_MODE_SIZE (is_mode)
-                 - GET_MODE_SIZE (wanted_mem_mode) - offset);
-#endif
-
       /* If bytes are big endian and we had a paradoxical SUBREG, we must
         adjust OFFSET to compensate. */
 #if BYTES_BIG_ENDIAN
@@ -4194,12 +4975,18 @@ make_extraction (mode, inner, pos, pos_rtx, len,
 #endif
 
       /* If this is a constant position, we can move to the desired byte.  */
-      if (pos >= 0)
+      if (pos_rtx == 0)
        {
          offset += pos / BITS_PER_UNIT;
          pos %= GET_MODE_BITSIZE (wanted_mem_mode);
        }
 
+#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN
+      if (! spans_byte && is_mode != wanted_mem_mode)
+       offset = (GET_MODE_SIZE (is_mode)
+                 - GET_MODE_SIZE (wanted_mem_mode) - offset);
+#endif
+
       if (offset != 0 || inner_mode != wanted_mem_mode)
        {
          rtx newmem = gen_rtx (MEM, wanted_mem_mode,
@@ -4213,19 +5000,27 @@ make_extraction (mode, inner, pos, pos_rtx, len,
 
   /* If INNER is not memory, we can always get it into the proper mode. */
   else if (GET_CODE (inner) != MEM)
-    inner = gen_lowpart_for_combine (extraction_mode, inner);
+    inner = force_to_mode (inner, extraction_mode,
+                          (pos < 0 ? GET_MODE_BITSIZE (extraction_mode)
+                           : len + pos),
+                          NULL_RTX);
 
   /* Adjust mode of POS_RTX, if needed.  If we want a wider mode, we
      have to zero extend.  Otherwise, we can just use a SUBREG.  */
-  if (pos < 0
+  if (pos_rtx != 0
       && GET_MODE_SIZE (pos_mode) > GET_MODE_SIZE (GET_MODE (pos_rtx)))
     pos_rtx = gen_rtx_combine (ZERO_EXTEND, pos_mode, pos_rtx);
-  else if (pos < 0
+  else if (pos_rtx != 0
           && GET_MODE_SIZE (pos_mode) < GET_MODE_SIZE (GET_MODE (pos_rtx)))
     pos_rtx = gen_lowpart_for_combine (pos_mode, pos_rtx);
 
-  /* Make POS_RTX unless we already have it and it is correct.  */
-  if (pos_rtx == 0 || (pos >= 0 && INTVAL (pos_rtx) != pos))
+  /* Make POS_RTX unless we already have it and it is correct.  If we don't
+     have a POS_RTX but we do have an ORIG_POS_RTX, the latter must
+     be a CONST_INT. */
+  if (pos_rtx == 0 && orig_pos_rtx != 0 && INTVAL (orig_pos_rtx) == pos)
+    pos_rtx = orig_pos_rtx;
+
+  else if (pos_rtx == 0)
     pos_rtx = GEN_INT (pos);
 
   /* Make the required operation.  See if we can use existing rtx.  */
@@ -4264,8 +5059,9 @@ make_compound_operation (x, in_code)
   enum machine_mode mode = GET_MODE (x);
   int mode_width = GET_MODE_BITSIZE (mode);
   enum rtx_code next_code;
-  int i;
+  int i, count;
   rtx new = 0;
+  rtx tem;
   char *fmt;
 
   /* Select the code to be used in recursive calls.  Once we are inside an
@@ -4289,9 +5085,12 @@ make_compound_operation (x, in_code)
       if (in_code == MEM && GET_CODE (XEXP (x, 1)) == CONST_INT
          && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT
          && INTVAL (XEXP (x, 1)) >= 0)
-       new = gen_rtx_combine (MULT, mode, XEXP (x, 0),
-                              GEN_INT ((HOST_WIDE_INT) 1
-                                       << INTVAL (XEXP (x, 1))));
+       {
+         new = make_compound_operation (XEXP (x, 0), next_code);
+         new = gen_rtx_combine (MULT, mode, new,
+                                GEN_INT ((HOST_WIDE_INT) 1
+                                         << INTVAL (XEXP (x, 1))));
+       }
       break;
 
     case AND:
@@ -4304,20 +5103,24 @@ make_compound_operation (x, in_code)
         is a logical right shift, make an extraction.  */
       if (GET_CODE (XEXP (x, 0)) == LSHIFTRT
          && (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0)
-       new = make_extraction (mode, XEXP (XEXP (x, 0), 0), -1,
-                              XEXP (XEXP (x, 0), 1), i, 1,
-                              0, in_code == COMPARE);
+       {
+         new = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code);
+         new = make_extraction (mode, new, 0, XEXP (XEXP (x, 0), 1), i, 1,
+                                0, in_code == COMPARE);
+       }
 
       /* Same as previous, but for (subreg (lshiftrt ...)) in first op.  */
       else if (GET_CODE (XEXP (x, 0)) == SUBREG
               && subreg_lowpart_p (XEXP (x, 0))
               && GET_CODE (SUBREG_REG (XEXP (x, 0))) == LSHIFTRT
               && (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0)
-       new = make_extraction (GET_MODE (SUBREG_REG (XEXP (x, 0))),
-                              XEXP (SUBREG_REG (XEXP (x, 0)), 0), -1,
-                              XEXP (SUBREG_REG (XEXP (x, 0)), 1), i, 1,
-                              0, in_code == COMPARE);
-
+       {
+         new = make_compound_operation (XEXP (SUBREG_REG (XEXP (x, 0)), 0),
+                                        next_code);
+         new = make_extraction (GET_MODE (SUBREG_REG (XEXP (x, 0))), new, 0,
+                                XEXP (SUBREG_REG (XEXP (x, 0)), 1), i, 1,
+                                0, in_code == COMPARE);
+       }
 
       /* If we are have (and (rotate X C) M) and C is larger than the number
         of bits in M, this is an extraction.  */
@@ -4326,25 +5129,20 @@ make_compound_operation (x, in_code)
               && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
               && (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0
               && i <= INTVAL (XEXP (XEXP (x, 0), 1)))
-       new = make_extraction (mode, XEXP (XEXP (x, 0), 0),
-                              (GET_MODE_BITSIZE (mode)
-                               - INTVAL (XEXP (XEXP (x, 0), 1))),
-                              NULL_RTX, i, 1, 0, in_code == COMPARE);
+       {
+         new = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code);
+         new = make_extraction (mode, new,
+                                (GET_MODE_BITSIZE (mode)
+                                 - INTVAL (XEXP (XEXP (x, 0), 1))),
+                                NULL_RTX, i, 1, 0, in_code == COMPARE);
+       }
 
       /* On machines without logical shifts, if the operand of the AND is
         a logical shift and our mask turns off all the propagated sign
         bits, we can replace the logical shift with an arithmetic shift.  */
-      else if (
-#ifdef HAVE_ashrsi3
-              HAVE_ashrsi3
-#else
-              0
-#endif
-#ifdef HAVE_lshrsi3
-              && ! HAVE_lshrsi3
-#else
-              && 1
-#endif
+      else if (ashr_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing
+              && (lshr_optab->handlers[(int) mode].insn_code
+                  == CODE_FOR_nothing)
               && GET_CODE (XEXP (x, 0)) == LSHIFTRT
               && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
               && INTVAL (XEXP (XEXP (x, 0), 1)) >= 0
@@ -4356,7 +5154,9 @@ make_compound_operation (x, in_code)
          mask >>= INTVAL (XEXP (XEXP (x, 0), 1));
          if ((INTVAL (XEXP (x, 1)) & ~mask) == 0)
            SUBST (XEXP (x, 0),
-                  gen_rtx_combine (ASHIFTRT, mode, XEXP (XEXP (x, 0), 0),
+                  gen_rtx_combine (ASHIFTRT, mode,
+                                   make_compound_operation (XEXP (XEXP (x, 0), 0),
+                                                            next_code),
                                    XEXP (XEXP (x, 0), 1)));
        }
 
@@ -4365,36 +5165,34 @@ make_compound_operation (x, in_code)
         If it doesn't end up being a ZERO_EXTEND, we will ignore it unless
         we are in a COMPARE.  */
       else if ((i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0)
-       new = make_extraction (mode, XEXP (x, 0), 0, NULL_RTX, i, 1,
-                              0, in_code == COMPARE);
+       new = make_extraction (mode,
+                              make_compound_operation (XEXP (x, 0),
+                                                       next_code),
+                              0, NULL_RTX, i, 1, 0, in_code == COMPARE);
 
       /* If we are in a comparison and this is an AND with a power of two,
         convert this into the appropriate bit extract.  */
       else if (in_code == COMPARE
               && (i = exact_log2 (INTVAL (XEXP (x, 1)))) >= 0)
-       new = make_extraction (mode, XEXP (x, 0), i, NULL_RTX, 1, 1, 0, 1);
+       new = make_extraction (mode,
+                              make_compound_operation (XEXP (x, 0),
+                                                       next_code),
+                              i, NULL_RTX, 1, 1, 0, 1);
 
       break;
 
     case LSHIFTRT:
       /* If the sign bit is known to be zero, replace this with an
         arithmetic shift.  */
-      if (
-#ifdef HAVE_ashrsi3
-         HAVE_ashrsi3
-#else
-         0
-#endif
-#ifdef HAVE_lshrsi3
-         && ! HAVE_lshrsi3
-#else
-         && 1
-#endif
+      if (ashr_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing
+         && lshr_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing
          && mode_width <= HOST_BITS_PER_WIDE_INT
-         && (significant_bits (XEXP (x, 0), mode)
-             & (1 << (mode_width - 1))) == 0)
+         && (nonzero_bits (XEXP (x, 0), mode) & (1 << (mode_width - 1))) == 0)
        {
-         new = gen_rtx_combine (ASHIFTRT, mode, XEXP (x, 0), XEXP (x, 1));
+         new = gen_rtx_combine (ASHIFTRT, mode,
+                                make_compound_operation (XEXP (x, 0),
+                                                         next_code),
+                                XEXP (x, 1));
          break;
        }
 
@@ -4407,17 +5205,103 @@ make_compound_operation (x, in_code)
          && GET_CODE (XEXP (x, 0)) == ASHIFT
          && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
          && INTVAL (XEXP (x, 1)) >= INTVAL (XEXP (XEXP (x, 0), 1)))
-       new = make_extraction (mode, XEXP (XEXP (x, 0), 0),
-                              (INTVAL (XEXP (x, 1))
-                               - INTVAL (XEXP (XEXP (x, 0), 1))),
-                              NULL_RTX, mode_width - INTVAL (XEXP (x, 1)),
-                              code == LSHIFTRT, 0, in_code == COMPARE);
+       {
+         new = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code);
+         new = make_extraction (mode, new,
+                                (INTVAL (XEXP (x, 1))
+                                 - INTVAL (XEXP (XEXP (x, 0), 1))),
+                                NULL_RTX, mode_width - INTVAL (XEXP (x, 1)),
+                                code == LSHIFTRT, 0, in_code == COMPARE);
+       }
+
+      /* Similarly if we have (ashifrt (OP (ashift foo C1) C3) C2).  In these
+        cases, we are better off returning a SIGN_EXTEND of the operation.  */
+
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+         && (GET_CODE (XEXP (x, 0)) == IOR || GET_CODE (XEXP (x, 0)) == AND
+             || GET_CODE (XEXP (x, 0)) == XOR
+             || GET_CODE (XEXP (x, 0)) == PLUS)
+         && GET_CODE (XEXP (XEXP (x, 0), 0)) == ASHIFT
+         && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT
+         && INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1)) < HOST_BITS_PER_WIDE_INT
+         && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+         && 0 == (INTVAL (XEXP (XEXP (x, 0), 1))
+                  & (((HOST_WIDE_INT) 1
+                      << (MIN (INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1)),
+                               INTVAL (XEXP (x, 1)))
+                          - 1)))))
+       {
+         rtx c1 = XEXP (XEXP (XEXP (x, 0), 0), 1);
+         rtx c2 = XEXP (x, 1);
+         rtx c3 = XEXP (XEXP (x, 0), 1);
+         HOST_WIDE_INT newop1;
+         rtx inner = XEXP (XEXP (XEXP (x, 0), 0), 0);
+
+         /* If C1 > C2, INNER needs to have the shift performed on it
+            for C1-C2 bits.  */
+         if (INTVAL (c1) > INTVAL (c2))
+           {
+             inner = gen_binary (ASHIFT, mode, inner,
+                                 GEN_INT (INTVAL (c1) - INTVAL (c2)));
+             c1 = c2;
+           }
+
+         newop1 = INTVAL (c3) >> INTVAL (c1);
+         new = make_compound_operation (inner,
+                                        GET_CODE (XEXP (x, 0)) == PLUS
+                                        ? MEM : GET_CODE (XEXP (x, 0)));
+         new = make_extraction (mode,
+                                gen_binary (GET_CODE (XEXP (x, 0)), mode, new,
+                                            GEN_INT (newop1)),
+                                INTVAL (c2) - INTVAL (c1),
+                                NULL_RTX, mode_width - INTVAL (c2),
+                                code == LSHIFTRT, 0, in_code == COMPARE);
+       }
+
+      /* Similarly for (ashiftrt (neg (ashift FOO C1)) C2).  */
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+         && GET_CODE (XEXP (x, 0)) == NEG
+         && GET_CODE (XEXP (XEXP (x, 0), 0)) == ASHIFT
+         && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == CONST_INT
+         && INTVAL (XEXP (x, 1)) >= INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1)))
+       {
+         new = make_compound_operation (XEXP (XEXP (XEXP (x, 0), 0), 0),
+                                        next_code);
+         new = make_extraction (mode,
+                                gen_unary (GET_CODE (XEXP (x, 0)), mode,
+                                           new, 0),
+                                (INTVAL (XEXP (x, 1))
+                                 - INTVAL (XEXP (XEXP (XEXP (x, 0), 0), 1))),
+                                NULL_RTX, mode_width - INTVAL (XEXP (x, 1)),
+                                code == LSHIFTRT, 0, in_code == COMPARE);
+       }
       break;
+
+    case SUBREG:
+      /* Call ourselves recursively on the inner expression.  If we are
+        narrowing the object and it has a different RTL code from
+        what it originally did, do this SUBREG as a force_to_mode.  */
+
+      tem = make_compound_operation (SUBREG_REG (x), in_code);
+      if (GET_CODE (tem) != GET_CODE (SUBREG_REG (x))
+         && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (tem))
+         && subreg_lowpart_p (x))
+       {
+         rtx newer = force_to_mode (tem, mode,
+                                    GET_MODE_BITSIZE (mode), NULL_RTX);
+
+         /* If we have something other than a SUBREG, we might have
+            done an expansion, so rerun outselves.  */
+         if (GET_CODE (newer) != SUBREG)
+           newer = make_compound_operation (newer, in_code);
+
+         return newer;
+       }
     }
 
   if (new)
     {
-      x = new;
+      x = gen_lowpart_for_combine (mode, new);
       code = GET_CODE (x);
     }
 
@@ -4476,6 +5360,7 @@ force_to_mode (x, mode, bits, reg)
      rtx reg;
 {
   enum rtx_code code = GET_CODE (x);
+  enum machine_mode op_mode = mode;
 
   /* If X is narrower than MODE or if BITS is larger than the size of MODE,
      just get X in the proper mode.  */
@@ -4532,7 +5417,13 @@ force_to_mode (x, mode, bits, reg)
          if (bits < HOST_BITS_PER_WIDE_INT)
            mask &= ((HOST_WIDE_INT) 1 << bits) - 1;
 
-         x = simplify_and_const_int (x, mode, op, mask);
+         /* If we have no AND in MODE, use the original mode for the
+            operation.  */
+
+         if (and_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+           op_mode = GET_MODE (x);
+
+         x = simplify_and_const_int (x, op_mode, op, mask);
 
          /* If X is still an AND, see if it is an AND with a mask that
             is just some low-order bits.  If so, and it is BITS wide (it
@@ -4542,7 +5433,8 @@ force_to_mode (x, mode, bits, reg)
              && bits < HOST_BITS_PER_WIDE_INT
              && INTVAL (XEXP (x, 1)) == ((HOST_WIDE_INT) 1 << bits) - 1)
            x = XEXP (x, 0);
-         return x;
+
+         break;
        }
 
       /* ... fall through ... */
@@ -4553,49 +5445,225 @@ force_to_mode (x, mode, bits, reg)
     case IOR:
     case XOR:
       /* For most binary operations, just propagate into the operation and
-        change the mode.  */
-
-      return gen_binary (code, mode,
-                        force_to_mode (XEXP (x, 0), mode, bits, reg),
-                        force_to_mode (XEXP (x, 1), mode, bits, reg));
+        change the mode if we have an operation of that mode.  */
+
+      if ((code == PLUS
+          && add_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+         || (code == MINUS
+             && sub_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+         || (code == MULT && (smul_optab->handlers[(int) mode].insn_code
+                              == CODE_FOR_nothing))
+         || (code == AND
+             && and_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+         || (code == IOR
+             && ior_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+         || (code == XOR && (xor_optab->handlers[(int) mode].insn_code
+                             == CODE_FOR_nothing)))
+       op_mode = GET_MODE (x);
+
+      x = gen_binary (code, op_mode,
+                     gen_lowpart_for_combine (op_mode,
+                                              force_to_mode (XEXP (x, 0),
+                                                             mode, bits,
+                                                             reg)),
+                     gen_lowpart_for_combine (op_mode,
+                                              force_to_mode (XEXP (x, 1),
+                                                             mode, bits,
+                                                             reg)));
+      break;
 
     case ASHIFT:
     case LSHIFT:
       /* For left shifts, do the same, but just for the first operand.
-        If the shift count is a constant, we need even fewer bits of the
-        first operand.  */
-
+        However, we cannot do anything with shifts where we cannot
+        guarantee that the counts are smaller than the size of the mode
+        because such a count will have a different meaning in a
+        wider mode.
+
+        If we can narrow the shift and know the count, we need even fewer
+        bits of the first operand.  */
+
+      if (! (GET_CODE (XEXP (x, 1)) == CONST_INT
+            && INTVAL (XEXP (x, 1)) < GET_MODE_BITSIZE (mode))
+         && ! (GET_MODE (XEXP (x, 1)) != VOIDmode
+               && (nonzero_bits (XEXP (x, 1), GET_MODE (XEXP (x, 1)))
+                   < (unsigned HOST_WIDE_INT) GET_MODE_BITSIZE (mode))))
+       break;
+       
       if (GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) < bits)
        bits -= INTVAL (XEXP (x, 1));
 
-      return gen_binary (code, mode,
-                        force_to_mode (XEXP (x, 0), mode, bits, reg),
-                        XEXP (x, 1));
+      if ((code == ASHIFT
+          && ashl_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+         || (code == LSHIFT && (lshl_optab->handlers[(int) mode].insn_code
+                                == CODE_FOR_nothing)))
+       op_mode = GET_MODE (x);
+
+      x =  gen_binary (code, op_mode,
+                      gen_lowpart_for_combine (op_mode,
+                                               force_to_mode (XEXP (x, 0),
+                                                              mode, bits,
+                                                              reg)),
+                      XEXP (x, 1));
+      break;
 
     case LSHIFTRT:
       /* Here we can only do something if the shift count is a constant and
-        the count plus BITS is no larger than the width of MODE, we can do
-        the shift in MODE.  */
+        the count plus BITS is no larger than the width of MODE.  In that
+        case, we can do the shift in MODE.  */
 
       if (GET_CODE (XEXP (x, 1)) == CONST_INT
          && INTVAL (XEXP (x, 1)) + bits <= GET_MODE_BITSIZE (mode))
-       return gen_binary (LSHIFTRT, mode,
-                          force_to_mode (XEXP (x, 0), mode,
-                                         bits + INTVAL (XEXP (x, 1)), reg),
-                          XEXP (x, 1));
+       {
+         rtx inner = force_to_mode (XEXP (x, 0), mode,
+                                    bits + INTVAL (XEXP (x, 1)), reg);
+
+         if (lshr_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+           op_mode = GET_MODE (x);
+
+         x = gen_binary (LSHIFTRT, op_mode,
+                         gen_lowpart_for_combine (op_mode, inner),
+                         XEXP (x, 1));
+       }
+      break;
+
+    case ASHIFTRT:
+      /* If this is a sign-extension operation that just affects bits
+        we don't care about, remove it.  */
+
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+         && INTVAL (XEXP (x, 1)) >= 0
+         && INTVAL (XEXP (x, 1)) <= GET_MODE_BITSIZE (GET_MODE (x)) - bits
+         && GET_CODE (XEXP (x, 0)) == ASHIFT
+         && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+         && INTVAL (XEXP (XEXP (x, 0), 1)) == INTVAL (XEXP (x, 1)))
+       return force_to_mode (XEXP (XEXP (x, 0), 0), mode, bits, reg);
       break;
 
     case NEG:
     case NOT:
+      if ((code == NEG
+          && neg_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+         || (code == NOT && (one_cmpl_optab->handlers[(int) mode].insn_code
+                             == CODE_FOR_nothing)))
+       op_mode = GET_MODE (x);
+
       /* Handle these similarly to the way we handle most binary operations. */
-      return gen_unary (code, mode,
-                       force_to_mode (XEXP (x, 0), mode, bits, reg));
+      x = gen_unary (code, op_mode,
+                    gen_lowpart_for_combine (op_mode,
+                                             force_to_mode (XEXP (x, 0), mode,
+                                                            bits, reg)));
+      break;
+
+    case IF_THEN_ELSE:
+      /* We have no way of knowing if the IF_THEN_ELSE can itself be
+        written in a narrower mode.  We play it safe and do not do so.  */
+
+      SUBST (XEXP (x, 1),
+            gen_lowpart_for_combine (GET_MODE (x),
+                                     force_to_mode (XEXP (x, 1), mode,
+                                                    bits, reg)));
+      SUBST (XEXP (x, 2),
+            gen_lowpart_for_combine (GET_MODE (x),
+                                     force_to_mode (XEXP (x, 2), mode,
+                                                    bits, reg)));
+      break;
     }
 
-  /* Otherwise, just do the operation canonically.  */
+  /* Ensure we return a value of the proper mode.  */
   return gen_lowpart_for_combine (mode, x);
 }
 \f
+/* Return the value of expression X given the fact that condition COND
+   is known to be true when applied to REG as its first operand and VAL
+   as its second.  X is known to not be shared and so can be modified in
+   place.
+
+   We only handle the simplest cases, and specifically those cases that
+   arise with IF_THEN_ELSE expressions.  */
+
+static rtx
+known_cond (x, cond, reg, val)
+     rtx x;
+     enum rtx_code cond;
+     rtx reg, val;
+{
+  enum rtx_code code = GET_CODE (x);
+  rtx new, temp;
+  char *fmt;
+  int i, j;
+
+  if (side_effects_p (x))
+    return x;
+
+  if (cond == EQ && rtx_equal_p (x, reg))
+    return val;
+
+  /* If X is (abs REG) and we know something about REG's relationship
+     with zero, we may be able to simplify this.  */
+
+  if (code == ABS && rtx_equal_p (XEXP (x, 0), reg) && val == const0_rtx)
+    switch (cond)
+      {
+      case GE:  case GT:  case EQ:
+       return XEXP (x, 0);
+      case LT:  case LE:
+       return gen_unary (NEG, GET_MODE (XEXP (x, 0)), XEXP (x, 0));
+      }
+
+  /* The only other cases we handle are MIN, MAX, and comparisons if the
+     operands are the same as REG and VAL.  */
+
+  else if (GET_RTX_CLASS (code) == '<' || GET_RTX_CLASS (code) == 'c')
+    {
+      if (rtx_equal_p (XEXP (x, 0), val))
+       cond = swap_condition (cond), temp = val, val = reg, reg = temp;
+
+      if (rtx_equal_p (XEXP (x, 0), reg) && rtx_equal_p (XEXP (x, 1), val))
+       {
+         if (GET_RTX_CLASS (code) == '<')
+           return (comparison_dominates_p (cond, code) ? const_true_rtx
+                   : (comparison_dominates_p (cond,
+                                              reverse_condition (code))
+                      ? const0_rtx : x));
+
+         else if (code == SMAX || code == SMIN
+                  || code == UMIN || code == UMAX)
+           {
+             int unsignedp = (code == UMIN || code == UMAX);
+
+             if (code == SMAX || code == UMAX)
+               cond = reverse_condition (cond);
+
+             switch (cond)
+               {
+               case GE:   case GT:
+                 return unsignedp ? x : XEXP (x, 1);
+               case LE:   case LT:
+                 return unsignedp ? x : XEXP (x, 0);
+               case GEU:  case GTU:
+                 return unsignedp ? XEXP (x, 1) : x;
+               case LEU:  case LTU:
+                 return unsignedp ? XEXP (x, 0) : x;
+               }
+           }
+       }
+    }
+
+  fmt = GET_RTX_FORMAT (code);
+  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'e')
+       SUBST (XEXP (x, i), known_cond (XEXP (x, i), cond, reg, val));
+      else if (fmt[i] == 'E')
+       for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+         SUBST (XVECEXP (x, i, j), known_cond (XVECEXP (x, i, j),
+                                               cond, reg, val));
+    }
+
+  return x;
+}
+\f
 /* See if X, a SET operation, can be rewritten as a bit-field assignment.
    Return that assignment if so.
 
@@ -4626,7 +5694,7 @@ make_field_assignment (x)
          || rtx_equal_p (dest, get_last_value (XEXP (src, 1)))
          || rtx_equal_p (get_last_value (dest), XEXP (src, 1))))
     {
-      assign = make_extraction (VOIDmode, dest, -1, XEXP (XEXP (src, 0), 1),
+      assign = make_extraction (VOIDmode, dest, 0, XEXP (XEXP (src, 0), 1),
                                1, 1, 1, 0);
       return gen_rtx (SET, VOIDmode, assign, const0_rtx);
     }
@@ -4641,7 +5709,7 @@ make_field_assignment (x)
               || rtx_equal_p (dest, get_last_value (XEXP (src, 1)))
               || rtx_equal_p (get_last_value (dest), XEXP (src, 1))))
     {
-      assign = make_extraction (VOIDmode, dest, -1,
+      assign = make_extraction (VOIDmode, dest, 0,
                                XEXP (SUBREG_REG (XEXP (src, 0)), 1),
                                1, 1, 1, 0);
       return gen_rtx (SET, VOIDmode, assign, const0_rtx);
@@ -4655,7 +5723,7 @@ make_field_assignment (x)
               || rtx_equal_p (dest, get_last_value (XEXP (src, 1)))
               || rtx_equal_p (get_last_value (dest), XEXP (src, 1))))
     {
-      assign = make_extraction (VOIDmode, dest, -1, XEXP (XEXP (src, 0), 1),
+      assign = make_extraction (VOIDmode, dest, 0, XEXP (XEXP (src, 0), 1),
                                1, 1, 1, 0);
       return gen_rtx (SET, VOIDmode, assign, const1_rtx);
     }
@@ -4686,7 +5754,8 @@ make_field_assignment (x)
 
   pos = get_pos_from_mask (~c1, &len);
   if (pos < 0 || pos + len > GET_MODE_BITSIZE (GET_MODE (dest))
-      || (c1 & significant_bits (other, GET_MODE (other))) != 0)
+      || (GET_MODE_BITSIZE (GET_MODE (other)) <= HOST_BITS_PER_WIDE_INT
+         && (c1 & nonzero_bits (other, GET_MODE (other))) != 0))
     return x;
 
   assign = make_extraction (VOIDmode, dest, pos, NULL_RTX, len, 1, 1, 0);
@@ -4718,6 +5787,12 @@ apply_distributive_law (x)
   rtx tem;
   enum rtx_code inner_code;
 
+  /* Distributivity is not true for floating point.
+     It can change the value.  So don't do it.
+     -- rms and moshier@world.std.com.  */
+  if (FLOAT_MODE_P (GET_MODE (x)))
+    return x;
+
   /* The outer operation can only be one of the following:  */
   if (code != IOR && code != AND && code != XOR
       && code != PLUS && code != MINUS)
@@ -4839,7 +5914,7 @@ simplify_and_const_int (x, mode, varop, constop)
 {
   register enum machine_mode tmode;
   register rtx temp;
-  unsigned HOST_WIDE_INT significant;
+  unsigned HOST_WIDE_INT nonzero;
 
   /* There is a large class of optimizations based on the principle that
      some operations produce results where certain bits are known to be zero,
@@ -4886,11 +5961,11 @@ simplify_and_const_int (x, mode, varop, constop)
        case SUBREG:
          if (subreg_lowpart_p (varop)
              /* We can ignore the effect this SUBREG if it narrows the mode
-                or, on machines where byte operations zero extend, if the
+                or, on machines where byte operations extend, if the
                 constant masks to zero all the bits the mode doesn't have.  */
              && ((GET_MODE_SIZE (GET_MODE (varop))
                   < GET_MODE_SIZE (GET_MODE (SUBREG_REG (varop))))
-#ifdef BYTE_LOADS_ZERO_EXTEND
+#ifdef BYTE_LOADS_EXTEND
                  || (0 == (constop
                            & GET_MODE_MASK (GET_MODE (varop))
                            & ~ GET_MODE_MASK (GET_MODE (SUBREG_REG (varop)))))
@@ -4930,16 +6005,20 @@ simplify_and_const_int (x, mode, varop, constop)
        case XOR:
          /* If VAROP is (ior (lshiftrt FOO C1) C2), try to commute the IOR and
             LSHIFT so we end up with an (and (lshiftrt (ior ...) ...) ...)
-            operation which may be a bitfield extraction.  */
+            operation which may be a bitfield extraction.  Ensure
+            that the constant we form is not wider than the mode of
+            VAROP.  */
 
          if (GET_CODE (XEXP (varop, 0)) == LSHIFTRT
              && GET_CODE (XEXP (XEXP (varop, 0), 1)) == CONST_INT
              && INTVAL (XEXP (XEXP (varop, 0), 1)) >= 0
              && INTVAL (XEXP (XEXP (varop, 0), 1)) < HOST_BITS_PER_WIDE_INT
              && GET_CODE (XEXP (varop, 1)) == CONST_INT
+             && ((INTVAL (XEXP (XEXP (varop, 0), 1))
+                 + floor_log2 (INTVAL (XEXP (varop, 1))))
+                 < GET_MODE_BITSIZE (GET_MODE (varop)))
              && (INTVAL (XEXP (varop, 1))
-                 & ~ significant_bits (XEXP (varop, 0),
-                                       GET_MODE (varop)) == 0))
+                 & ~ nonzero_bits (XEXP (varop, 0), GET_MODE (varop)) == 0))
            {
              temp = GEN_INT ((INTVAL (XEXP (varop, 1)) & constop)
                              << INTVAL (XEXP (XEXP (varop, 0), 1)));
@@ -4966,12 +6045,15 @@ simplify_and_const_int (x, mode, varop, constop)
                                         XEXP (varop, 1), constop))));
 
        case NOT:
-         /* (and (not FOO)) is (and (xor FOO CONST_OP)) so if FOO is an
-            LSHIFTRT we can do the same as above.  */
+         /* (and (not FOO)) is (and (xor FOO CONST)), so if FOO is an
+            LSHIFTRT, we can do the same as above.  Ensure that the constant
+            we form is not wider than the mode of VAROP.  */
 
          if (GET_CODE (XEXP (varop, 0)) == LSHIFTRT
              && GET_CODE (XEXP (XEXP (varop, 0), 1)) == CONST_INT
              && INTVAL (XEXP (XEXP (varop, 0), 1)) >= 0
+             && (INTVAL (XEXP (XEXP (varop, 0), 1)) + floor_log2 (constop)
+                 < GET_MODE_BITSIZE (GET_MODE (varop)))
              && INTVAL (XEXP (XEXP (varop, 0), 1)) < HOST_BITS_PER_WIDE_INT)
            {
              temp = GEN_INT (constop << INTVAL (XEXP (XEXP (varop, 0), 1)));
@@ -5006,10 +6088,10 @@ simplify_and_const_int (x, mode, varop, constop)
            {
              int i = -1;
 
-             significant = GET_MODE_MASK (GET_MODE (varop));
-             significant >>= INTVAL (XEXP (varop, 1));
+             nonzero = GET_MODE_MASK (GET_MODE (varop));
+             nonzero >>= INTVAL (XEXP (varop, 1));
 
-             if ((constop & ~significant) == 0
+             if ((constop & ~ nonzero) == 0
                  || (i = exact_log2 (constop)) >= 0)
                {
                  varop = simplify_shift_const
@@ -5028,13 +6110,33 @@ simplify_and_const_int (x, mode, varop, constop)
                                     XEXP (varop, 0), XEXP (varop, 1));
          break;
 
+       case LSHIFTRT:
+         /* If we have (and (lshiftrt FOO C1) C2) where the combination of the
+            shift and AND produces only copies of the sign bit (C2 is one less
+            than a power of two), we can do this with just a shift.  */
+
+         if (GET_CODE (XEXP (varop, 1)) == CONST_INT
+             && ((INTVAL (XEXP (varop, 1))
+                  + num_sign_bit_copies (XEXP (varop, 0),
+                                         GET_MODE (XEXP (varop, 0))))
+                 >= GET_MODE_BITSIZE (GET_MODE (varop)))
+             && exact_log2 (constop + 1) >= 0
+             && (num_sign_bit_copies (XEXP (varop, 0),
+                                      GET_MODE (XEXP (varop, 0)))
+                 >= exact_log2 (constop + 1)))
+           varop
+             = gen_rtx_combine (LSHIFTRT, GET_MODE (varop), XEXP (varop, 0),
+                                GEN_INT (GET_MODE_BITSIZE (GET_MODE (varop))
+                                         - exact_log2 (constop + 1)));
+         break;
+
        case NE:
          /* (and (ne FOO 0) CONST) can be (and FOO CONST) if CONST is
-            included in STORE_FLAG_VALUE and FOO has no significant bits
-            not in CONST.  */
+            included in STORE_FLAG_VALUE and FOO has no bits that might be
+            nonzero not in CONST.  */
          if ((constop & ~ STORE_FLAG_VALUE) == 0
              && XEXP (varop, 0) == const0_rtx
-             && (significant_bits (XEXP (varop, 0), mode) & ~ constop) == 0)
+             && (nonzero_bits (XEXP (varop, 0), mode) & ~ constop) == 0)
            {
              varop = XEXP (varop, 0);
              continue;
@@ -5048,7 +6150,7 @@ simplify_and_const_int (x, mode, varop, constop)
             and possibly the PLUS if it is now adding zero.  */
          if (GET_CODE (XEXP (varop, 1)) == CONST_INT
              && exact_log2 (-constop) >= 0
-             && (significant_bits (XEXP (varop, 0), mode) & ~ constop) == 0)
+             && (nonzero_bits (XEXP (varop, 0), mode) & ~ constop) == 0)
            {
              varop = plus_constant (XEXP (varop, 0),
                                     INTVAL (XEXP (varop, 1)) & constop);
@@ -5085,14 +6187,17 @@ simplify_and_const_int (x, mode, varop, constop)
   if (GET_CODE (varop) == CONST_INT)
     return GEN_INT (constop & INTVAL (varop));
 
-  /* See what bits are significant in VAROP.  */
-  significant = significant_bits (varop, mode);
+  /* See what bits may be nonzero in VAROP.  Unlike the general case of
+     a call to nonzero_bits, here we don't care about bits outside
+     MODE.  */
+
+  nonzero = nonzero_bits (varop, mode) & GET_MODE_MASK (mode);
 
   /* Turn off all bits in the constant that are known to already be zero.
-     Thus, if the AND isn't needed at all, we will have CONSTOP == SIGNIFICANT
+     Thus, if the AND isn't needed at all, we will have CONSTOP == NONZERO_BITS
      which is tested below.  */
 
-  constop &= significant;
+  constop &= nonzero;
 
   /* If we don't have any bits left, return zero.  */
   if (constop == 0)
@@ -5112,7 +6217,7 @@ simplify_and_const_int (x, mode, varop, constop)
     return x ? x : varop;
 
   /* If we are only masking insignificant bits, return VAROP.  */
-  if (constop == significant)
+  if (constop == nonzero)
     x = varop;
 
   /* Otherwise, return an AND.  See how much, if any, of X we can use.  */
@@ -5138,12 +6243,12 @@ simplify_and_const_int (x, mode, varop, constop)
    a shift, AND, or zero_extract, we can do better.  */
 
 static unsigned HOST_WIDE_INT
-significant_bits (x, mode)
+nonzero_bits (x, mode)
      rtx x;
      enum machine_mode mode;
 {
-  unsigned HOST_WIDE_INT significant = GET_MODE_MASK (mode);
-  unsigned HOST_WIDE_INT inner_sig;
+  unsigned HOST_WIDE_INT nonzero = GET_MODE_MASK (mode);
+  unsigned HOST_WIDE_INT inner_nz;
   enum rtx_code code;
   int mode_width = GET_MODE_BITSIZE (mode);
   rtx tem;
@@ -5152,14 +6257,14 @@ significant_bits (x, mode)
   if (GET_MODE_BITSIZE (GET_MODE (x)) > mode_width)
     {
       mode = GET_MODE (x);
-      significant = GET_MODE_MASK (mode);
+      nonzero = GET_MODE_MASK (mode);
       mode_width = GET_MODE_BITSIZE (mode);
     }
 
   if (mode_width > HOST_BITS_PER_WIDE_INT)
     /* Our only callers in this case look for single bit values.  So
        just return the mode mask.  Those tests will then be false.  */
-    return significant;
+    return nonzero;
 
   code = GET_CODE (x);
   switch (code)
@@ -5179,23 +6284,63 @@ significant_bits (x, mode)
          sp_alignment = MIN (PUSH_ROUNDING (1), sp_alignment);
 #endif
 
-         return significant & ~ (sp_alignment - 1);
+         return nonzero & ~ (sp_alignment - 1);
        }
 #endif
 
-      /* If X is a register whose value we can find, use that value.  
-        Otherwise, use the previously-computed significant bits for this
-        register.  */
+      /* If X is a register whose nonzero bits value is current, use it.
+        Otherwise, if X is a register whose value we can find, use that
+        value.  Otherwise, use the previously-computed global nonzero bits
+        for this register.  */
+
+      if (reg_last_set_value[REGNO (x)] != 0
+         && reg_last_set_mode[REGNO (x)] == mode
+         && (reg_n_sets[REGNO (x)] == 1
+             || reg_last_set_label[REGNO (x)] == label_tick)
+         && INSN_CUID (reg_last_set[REGNO (x)]) < subst_low_cuid)
+       return reg_last_set_nonzero_bits[REGNO (x)];
 
       tem = get_last_value (x);
+
       if (tem)
-       return significant_bits (tem, mode);
-      else if (significant_valid && reg_significant[REGNO (x)])
-       return reg_significant[REGNO (x)] & significant;
+       {
+#ifdef SHORT_IMMEDIATES_SIGN_EXTEND
+         /* If X is narrower than MODE and TEM is a non-negative
+            constant that would appear negative in the mode of X,
+            sign-extend it for use in reg_nonzero_bits because some
+            machines (maybe most) will actually do the sign-extension
+            and this is the conservative approach. 
+
+            ??? For 2.5, try to tighten up the MD files in this regard
+            instead of this kludge.  */
+
+         if (GET_MODE_BITSIZE (GET_MODE (x)) < mode_width
+             && GET_CODE (tem) == CONST_INT
+             && INTVAL (tem) > 0
+             && 0 != (INTVAL (tem)
+                      & ((HOST_WIDE_INT) 1
+                         << GET_MODE_BITSIZE (GET_MODE (x)))))
+           tem = GEN_INT (INTVAL (tem)
+                          | ((HOST_WIDE_INT) (-1)
+                             << GET_MODE_BITSIZE (GET_MODE (x))));
+#endif
+         return nonzero_bits (tem, mode);
+       }
+      else if (nonzero_sign_valid && reg_nonzero_bits[REGNO (x)])
+       return reg_nonzero_bits[REGNO (x)] & nonzero;
       else
-       return significant;
+       return nonzero;
 
     case CONST_INT:
+#ifdef SHORT_IMMEDIATES_SIGN_EXTEND
+      /* If X is negative in MODE, sign-extend the value.  */
+      if (INTVAL (x) > 0
+         && 0 != (INTVAL (x)
+                  & ((HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (GET_MODE (x)))))
+       return (INTVAL (x)
+               | ((HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (GET_MODE (x))));
+#endif
+
       return INTVAL (x);
 
 #ifdef BYTE_LOADS_ZERO_EXTEND
@@ -5203,7 +6348,7 @@ significant_bits (x, mode)
       /* In many, if not most, RISC machines, reading a byte from memory
         zeros the rest of the register.  Noticing that fact saves a lot
         of extra zero-extends.  */
-      significant &= GET_MODE_MASK (GET_MODE (x));
+      nonzero &= GET_MODE_MASK (GET_MODE (x));
       break;
 #endif
 
@@ -5215,65 +6360,67 @@ significant_bits (x, mode)
     case LE:  case LEU:
 
       if (GET_MODE_CLASS (mode) == MODE_INT)
-       significant = 1;
+       nonzero = 1;
 
       /* A comparison operation only sets the bits given by its mode.  The
         rest are set undefined.  */
       if (GET_MODE_SIZE (GET_MODE (x)) < mode_width)
-       significant |= (GET_MODE_MASK (mode) & ~ GET_MODE_MASK (GET_MODE (x)));
+       nonzero |= (GET_MODE_MASK (mode) & ~ GET_MODE_MASK (GET_MODE (x)));
       break;
 #endif
 
-#if STORE_FLAG_VALUE == -1
     case NEG:
-      if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
-         || ((tem = get_last_value (XEXP (x, 0))) != 0
-             && GET_RTX_CLASS (GET_CODE (tem)) == '<'))
-       significant = 1;
+      if (num_sign_bit_copies (XEXP (x, 0), GET_MODE (x))
+         == GET_MODE_BITSIZE (GET_MODE (x)))
+       nonzero = 1;
 
       if (GET_MODE_SIZE (GET_MODE (x)) < mode_width)
-       significant |= (GET_MODE_MASK (mode) & ~ GET_MODE_MASK (GET_MODE (x)));
+       nonzero |= (GET_MODE_MASK (mode) & ~ GET_MODE_MASK (GET_MODE (x)));
+      break;
+
+    case ABS:
+      if (num_sign_bit_copies (XEXP (x, 0), GET_MODE (x))
+         == GET_MODE_BITSIZE (GET_MODE (x)))
+       nonzero = 1;
       break;
-#endif
 
     case TRUNCATE:
-      significant &= (significant_bits (XEXP (x, 0), mode)
-                     & GET_MODE_MASK (mode));
+      nonzero &= (nonzero_bits (XEXP (x, 0), mode) & GET_MODE_MASK (mode));
       break;
 
     case ZERO_EXTEND:
-      significant &= significant_bits (XEXP (x, 0), mode);
+      nonzero &= nonzero_bits (XEXP (x, 0), mode);
       if (GET_MODE (XEXP (x, 0)) != VOIDmode)
-       significant &= GET_MODE_MASK (GET_MODE (XEXP (x, 0)));
+       nonzero &= GET_MODE_MASK (GET_MODE (XEXP (x, 0)));
       break;
 
     case SIGN_EXTEND:
       /* If the sign bit is known clear, this is the same as ZERO_EXTEND.
         Otherwise, show all the bits in the outer mode but not the inner
         may be non-zero.  */
-      inner_sig = significant_bits (XEXP (x, 0), mode);
+      inner_nz = nonzero_bits (XEXP (x, 0), mode);
       if (GET_MODE (XEXP (x, 0)) != VOIDmode)
        {
-         inner_sig &= GET_MODE_MASK (GET_MODE (XEXP (x, 0)));
-         if (inner_sig &
+         inner_nz &= GET_MODE_MASK (GET_MODE (XEXP (x, 0)));
+         if (inner_nz &
              (((HOST_WIDE_INT) 1
                << (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - 1))))
-           inner_sig |= (GET_MODE_MASK (mode)
+           inner_nz |= (GET_MODE_MASK (mode)
                          & ~ GET_MODE_MASK (GET_MODE (XEXP (x, 0))));
        }
 
-      significant &= inner_sig;
+      nonzero &= inner_nz;
       break;
 
     case AND:
-      significant &= (significant_bits (XEXP (x, 0), mode)
-                     & significant_bits (XEXP (x, 1), mode));
+      nonzero &= (nonzero_bits (XEXP (x, 0), mode)
+                 & nonzero_bits (XEXP (x, 1), mode));
       break;
 
-    case XOR:
-    case IOR:
-      significant &= (significant_bits (XEXP (x, 0), mode)
-                     | significant_bits (XEXP (x, 1), mode));
+    case XOR:   case IOR:
+    case UMIN:  case UMAX:  case SMIN:  case SMAX:
+      nonzero &= (nonzero_bits (XEXP (x, 0), mode)
+                 | nonzero_bits (XEXP (x, 1), mode));
       break;
 
     case PLUS:  case MINUS:
@@ -5285,14 +6432,14 @@ significant_bits (x, mode)
         computing the width (position of the highest-order non-zero bit)
         and the number of low-order zero bits for each value.  */
       {
-       unsigned HOST_WIDE_INT sig0 = significant_bits (XEXP (x, 0), mode);
-       unsigned HOST_WIDE_INT sig1 = significant_bits (XEXP (x, 1), mode);
-       int width0 = floor_log2 (sig0) + 1;
-       int width1 = floor_log2 (sig1) + 1;
-       int low0 = floor_log2 (sig0 & -sig0);
-       int low1 = floor_log2 (sig1 & -sig1);
-       int op0_maybe_minusp = (sig0 & (1 << (mode_width - 1)));
-       int op1_maybe_minusp = (sig1 & (1 << (mode_width - 1)));
+       unsigned HOST_WIDE_INT nz0 = nonzero_bits (XEXP (x, 0), mode);
+       unsigned HOST_WIDE_INT nz1 = nonzero_bits (XEXP (x, 1), mode);
+       int width0 = floor_log2 (nz0) + 1;
+       int width1 = floor_log2 (nz1) + 1;
+       int low0 = floor_log2 (nz0 & -nz0);
+       int low1 = floor_log2 (nz1 & -nz1);
+       int op0_maybe_minusp = (nz0 & ((HOST_WIDE_INT) 1 << (mode_width - 1)));
+       int op1_maybe_minusp = (nz1 & ((HOST_WIDE_INT) 1 << (mode_width - 1)));
        int result_width = mode_width;
        int result_low = 0;
 
@@ -5328,36 +6475,44 @@ significant_bits (x, mode)
          }
 
        if (result_width < mode_width)
-         significant &= ((HOST_WIDE_INT) 1 << result_width) - 1;
+         nonzero &= ((HOST_WIDE_INT) 1 << result_width) - 1;
 
        if (result_low > 0)
-         significant &= ~ (((HOST_WIDE_INT) 1 << result_low) - 1);
+         nonzero &= ~ (((HOST_WIDE_INT) 1 << result_low) - 1);
       }
       break;
 
     case ZERO_EXTRACT:
       if (GET_CODE (XEXP (x, 1)) == CONST_INT
          && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT)
-       significant &= ((HOST_WIDE_INT) 1 << INTVAL (XEXP (x, 1))) - 1;
+       nonzero &= ((HOST_WIDE_INT) 1 << INTVAL (XEXP (x, 1))) - 1;
       break;
 
     case SUBREG:
+      /* If this is a SUBREG formed for a promoted variable that has
+        been zero-extended, we know that at least the high-order bits
+        are zero, though others might be too.  */
+
+      if (SUBREG_PROMOTED_VAR_P (x) && SUBREG_PROMOTED_UNSIGNED_P (x))
+       nonzero = (GET_MODE_MASK (GET_MODE (x))
+                  & nonzero_bits (SUBREG_REG (x), GET_MODE (x)));
+
       /* If the inner mode is a single word for both the host and target
         machines, we can compute this from which bits of the inner
-        object are known significant.  */
+        object might be nonzero.  */
       if (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) <= BITS_PER_WORD
          && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x)))
              <= HOST_BITS_PER_WIDE_INT))
        {
-         significant &= significant_bits (SUBREG_REG (x), mode);
-#ifndef BYTE_LOADS_ZERO_EXTEND
+         nonzero &= nonzero_bits (SUBREG_REG (x), mode);
+#ifndef BYTE_LOADS_EXTEND
          /* On many CISC machines, accessing an object in a wider mode
             causes the high-order bits to become undefined.  So they are
             not known to be zero.  */
          if (GET_MODE_SIZE (GET_MODE (x))
              > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
-           significant |= (GET_MODE_MASK (GET_MODE (x))
-                           & ~ GET_MODE_MASK (GET_MODE (SUBREG_REG (x))));
+           nonzero |= (GET_MODE_MASK (GET_MODE (x))
+                       & ~ GET_MODE_MASK (GET_MODE (SUBREG_REG (x))));
 #endif
        }
       break;
@@ -5367,9 +6522,9 @@ significant_bits (x, mode)
     case ASHIFT:
     case LSHIFT:
     case ROTATE:
-      /* The significant bits are in two classes: any bits within MODE
+      /* The nonzero bits are in two classes: any bits within MODE
         that aren't in GET_MODE (x) are always significant.  The rest of the
-        significant bits are those that are significant in the operand of
+        nonzero bits are those that are significant in the operand of
         the shift when shifted the appropriate number of bits.  This
         shows that high-order bits are cleared by the right shift and
         low-order bits by left shifts.  */
@@ -5381,13 +6536,12 @@ significant_bits (x, mode)
          int width = GET_MODE_BITSIZE (inner_mode);
          int count = INTVAL (XEXP (x, 1));
          unsigned HOST_WIDE_INT mode_mask = GET_MODE_MASK (inner_mode);
-         unsigned HOST_WIDE_INT op_significant
-           = significant_bits (XEXP (x, 0), mode);
-         unsigned HOST_WIDE_INT inner = op_significant & mode_mask;
+         unsigned HOST_WIDE_INT op_nonzero = nonzero_bits (XEXP (x, 0), mode);
+         unsigned HOST_WIDE_INT inner = op_nonzero & mode_mask;
          unsigned HOST_WIDE_INT outer = 0;
 
          if (mode_width > width)
-           outer = (op_significant & significant & ~ mode_mask);
+           outer = (op_nonzero & nonzero & ~ mode_mask);
 
          if (code == LSHIFTRT)
            inner >>= count;
@@ -5395,9 +6549,9 @@ significant_bits (x, mode)
            {
              inner >>= count;
 
-             /* If the sign bit was significant at before the shift, we
+             /* If the sign bit may have been nonzero before the shift, we
                 need to mark all the places it could have been copied to
-                by the shift significant.  */
+                by the shift as possibly nonzero.  */
              if (inner & ((HOST_WIDE_INT) 1 << (width - 1 - count)))
                inner |= (((HOST_WIDE_INT) 1 << count) - 1) << (width - count);
            }
@@ -5407,17 +6561,315 @@ significant_bits (x, mode)
            inner = ((inner << (count % width)
                      | (inner >> (width - (count % width)))) & mode_mask);
 
-         significant &= (outer | inner);
+         nonzero &= (outer | inner);
        }
       break;
 
     case FFS:
       /* This is at most the number of bits in the mode.  */
-      significant = ((HOST_WIDE_INT) 1 << (floor_log2 (mode_width) + 1)) - 1;
+      nonzero = ((HOST_WIDE_INT) 1 << (floor_log2 (mode_width) + 1)) - 1;
+      break;
+
+    case IF_THEN_ELSE:
+      nonzero &= (nonzero_bits (XEXP (x, 1), mode)
+                 | nonzero_bits (XEXP (x, 2), mode));
+      break;
+    }
+
+  return nonzero;
+}
+\f
+/* Return the number of bits at the high-order end of X that are known to
+   be equal to the sign bit.  This number will always be between 1 and
+   the number of bits in the mode of X.  MODE is the mode to be used
+   if X is VOIDmode.  */
+
+static int
+num_sign_bit_copies (x, mode)
+     rtx x;
+     enum machine_mode mode;
+{
+  enum rtx_code code = GET_CODE (x);
+  int bitwidth;
+  int num0, num1, result;
+  unsigned HOST_WIDE_INT nonzero;
+  rtx tem;
+
+  /* If we weren't given a mode, use the mode of X.  If the mode is still
+     VOIDmode, we don't know anything.  */
+
+  if (mode == VOIDmode)
+    mode = GET_MODE (x);
+
+  if (mode == VOIDmode)
+    return 1;
+
+  bitwidth = GET_MODE_BITSIZE (mode);
+
+  switch (code)
+    {
+    case REG:
+
+      if (reg_last_set_value[REGNO (x)] != 0
+         && reg_last_set_mode[REGNO (x)] == mode
+         && (reg_n_sets[REGNO (x)] == 1
+             || reg_last_set_label[REGNO (x)] == label_tick)
+         && INSN_CUID (reg_last_set[REGNO (x)]) < subst_low_cuid)
+       return reg_last_set_sign_bit_copies[REGNO (x)];
+
+      tem =  get_last_value (x);
+      if (tem != 0)
+       return num_sign_bit_copies (tem, mode);
+
+      if (nonzero_sign_valid && reg_sign_bit_copies[REGNO (x)] != 0)
+       return reg_sign_bit_copies[REGNO (x)];
+      break;
+
+#ifdef BYTE_LOADS_SIGN_EXTEND
+    case MEM:
+      /* Some RISC machines sign-extend all loads of smaller than a word.  */
+      return MAX (1, bitwidth - GET_MODE_BITSIZE (GET_MODE (x)) + 1);
+#endif
+
+    case CONST_INT:
+      /* If the constant is negative, take its 1's complement and remask.
+        Then see how many zero bits we have.  */
+      nonzero = INTVAL (x) & GET_MODE_MASK (mode);
+      if (bitwidth <= HOST_BITS_PER_WIDE_INT
+         && (nonzero & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0)
+       nonzero = (~ nonzero) & GET_MODE_MASK (mode);
+
+      return (nonzero == 0 ? bitwidth : bitwidth - floor_log2 (nonzero) - 1);
+
+    case SUBREG:
+      /* If this is a SUBREG for a promoted object that is sign-extended
+        and we are looking at it in a wider mode, we know that at least the
+        high-order bits are known to be sign bit copies.  */
+
+      if (SUBREG_PROMOTED_VAR_P (x) && ! SUBREG_PROMOTED_UNSIGNED_P (x))
+       return MAX (bitwidth - GET_MODE_BITSIZE (GET_MODE (x)) + 1,
+                   num_sign_bit_copies (SUBREG_REG (x), mode));
+
+      /* For a smaller object, just ignore the high bits. */
+      if (bitwidth <= GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))))
+       {
+         num0 = num_sign_bit_copies (SUBREG_REG (x), VOIDmode);
+         return MAX (1, (num0
+                         - (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x)))
+                            - bitwidth)));
+       }
+
+#ifdef BYTE_LOADS_EXTEND
+      /* For paradoxical SUBREGs, just look inside since, on machines with
+        one of these defined, we assume that operations are actually 
+        performed on the full register.  Note that we are passing MODE
+        to the recursive call, so the number of sign bit copies will
+        remain relative to that mode, not the inner mode.  */
+
+      if (GET_MODE_SIZE (GET_MODE (x))
+         > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+       return num_sign_bit_copies (SUBREG_REG (x), mode);
+#endif
+
+      break;
+
+    case SIGN_EXTRACT:
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+       return MAX (1, bitwidth - INTVAL (XEXP (x, 1)));
+      break;
+
+    case SIGN_EXTEND: 
+      return (bitwidth - GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))
+             + num_sign_bit_copies (XEXP (x, 0), VOIDmode));
+
+    case TRUNCATE:
+      /* For a smaller object, just ignore the high bits. */
+      num0 = num_sign_bit_copies (XEXP (x, 0), VOIDmode);
+      return MAX (1, (num0 - (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))
+                             - bitwidth)));
+
+    case NOT:
+      return num_sign_bit_copies (XEXP (x, 0), mode);
+
+    case ROTATE:       case ROTATERT:
+      /* If we are rotating left by a number of bits less than the number
+        of sign bit copies, we can just subtract that amount from the
+        number.  */
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+         && INTVAL (XEXP (x, 1)) >= 0 && INTVAL (XEXP (x, 1)) < bitwidth)
+       {
+         num0 = num_sign_bit_copies (XEXP (x, 0), mode);
+         return MAX (1, num0 - (code == ROTATE ? INTVAL (XEXP (x, 1))
+                                : bitwidth - INTVAL (XEXP (x, 1))));
+       }
       break;
+
+    case NEG:
+      /* In general, this subtracts one sign bit copy.  But if the value
+        is known to be positive, the number of sign bit copies is the
+        same as that of the input.  Finally, if the input has just one bit
+        that might be nonzero, all the bits are copies of the sign bit.  */
+      nonzero = nonzero_bits (XEXP (x, 0), mode);
+      if (nonzero == 1)
+       return bitwidth;
+
+      num0 = num_sign_bit_copies (XEXP (x, 0), mode);
+      if (num0 > 1
+         && bitwidth <= HOST_BITS_PER_WIDE_INT
+         && (((HOST_WIDE_INT) 1 << (bitwidth - 1)) & nonzero))
+       num0--;
+
+      return num0;
+
+    case IOR:   case AND:   case XOR:
+    case SMIN:  case SMAX:  case UMIN:  case UMAX:
+      /* Logical operations will preserve the number of sign-bit copies.
+        MIN and MAX operations always return one of the operands.  */
+      num0 = num_sign_bit_copies (XEXP (x, 0), mode);
+      num1 = num_sign_bit_copies (XEXP (x, 1), mode);
+      return MIN (num0, num1);
+
+    case PLUS:  case MINUS:
+      /* For addition and subtraction, we can have a 1-bit carry.  However,
+        if we are subtracting 1 from a positive number, there will not
+        be such a carry.  Furthermore, if the positive number is known to
+        be 0 or 1, we know the result is either -1 or 0.  */
+
+      if (code == PLUS && XEXP (x, 1) == constm1_rtx
+         && bitwidth <= HOST_BITS_PER_WIDE_INT)
+       {
+         nonzero = nonzero_bits (XEXP (x, 0), mode);
+         if ((((HOST_WIDE_INT) 1 << (bitwidth - 1)) & nonzero) == 0)
+           return (nonzero == 1 || nonzero == 0 ? bitwidth
+                   : bitwidth - floor_log2 (nonzero) - 1);
+       }
+
+      num0 = num_sign_bit_copies (XEXP (x, 0), mode);
+      num1 = num_sign_bit_copies (XEXP (x, 1), mode);
+      return MAX (1, MIN (num0, num1) - 1);
+      
+    case MULT:
+      /* The number of bits of the product is the sum of the number of
+        bits of both terms.  However, unless one of the terms if known
+        to be positive, we must allow for an additional bit since negating
+        a negative number can remove one sign bit copy.  */
+
+      num0 = num_sign_bit_copies (XEXP (x, 0), mode);
+      num1 = num_sign_bit_copies (XEXP (x, 1), mode);
+
+      result = bitwidth - (bitwidth - num0) - (bitwidth - num1);
+      if (result > 0
+         && bitwidth <= HOST_BITS_PER_WIDE_INT
+         && ((nonzero_bits (XEXP (x, 0), mode)
+              & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0)
+         && (nonzero_bits (XEXP (x, 1), mode)
+             & ((HOST_WIDE_INT) 1 << (bitwidth - 1)) != 0))
+       result--;
+
+      return MAX (1, result);
+
+    case UDIV:
+      /* The result must be <= the first operand.  */
+      return num_sign_bit_copies (XEXP (x, 0), mode);
+
+    case UMOD:
+      /* The result must be <= the scond operand.  */
+      return num_sign_bit_copies (XEXP (x, 1), mode);
+
+    case DIV:
+      /* Similar to unsigned division, except that we have to worry about
+        the case where the divisor is negative, in which case we have
+        to add 1.  */
+      result = num_sign_bit_copies (XEXP (x, 0), mode);
+      if (result > 1
+         && bitwidth <= HOST_BITS_PER_WIDE_INT
+         && (nonzero_bits (XEXP (x, 1), mode)
+             & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0)
+       result --;
+
+      return result;
+
+    case MOD:
+      result = num_sign_bit_copies (XEXP (x, 1), mode);
+      if (result > 1
+         && bitwidth <= HOST_BITS_PER_WIDE_INT
+         && (nonzero_bits (XEXP (x, 1), mode)
+             & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0)
+       result --;
+
+      return result;
+
+    case ASHIFTRT:
+      /* Shifts by a constant add to the number of bits equal to the
+        sign bit.  */
+      num0 = num_sign_bit_copies (XEXP (x, 0), mode);
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+         && INTVAL (XEXP (x, 1)) > 0)
+       num0 = MIN (bitwidth, num0 + INTVAL (XEXP (x, 1)));
+
+      return num0;
+
+    case ASHIFT:
+    case LSHIFT:
+      /* Left shifts destroy copies.  */
+      if (GET_CODE (XEXP (x, 1)) != CONST_INT
+         || INTVAL (XEXP (x, 1)) < 0
+         || INTVAL (XEXP (x, 1)) >= bitwidth)
+       return 1;
+
+      num0 = num_sign_bit_copies (XEXP (x, 0), mode);
+      return MAX (1, num0 - INTVAL (XEXP (x, 1)));
+
+    case IF_THEN_ELSE:
+      num0 = num_sign_bit_copies (XEXP (x, 1), mode);
+      num1 = num_sign_bit_copies (XEXP (x, 2), mode);
+      return MIN (num0, num1);
+
+#if STORE_FLAG_VALUE == -1
+    case EQ:  case NE:  case GE:  case GT:  case LE:  case LT:
+    case GEU: case GTU: case LEU: case LTU:
+      return bitwidth;
+#endif
     }
 
-  return significant;
+  /* If we haven't been able to figure it out by one of the above rules,
+     see if some of the high-order bits are known to be zero.  If so,
+     count those bits and return one less than that amount.  If we can't
+     safely compute the mask for this mode, always return BITWIDTH.  */
+
+  if (bitwidth > HOST_BITS_PER_WIDE_INT)
+    return 1;
+
+  nonzero = nonzero_bits (x, mode);
+  return (nonzero & ((HOST_WIDE_INT) 1 << (bitwidth - 1))
+         ? 1 : bitwidth - floor_log2 (nonzero) - 1);
+}
+\f
+/* Return the number of "extended" bits there are in X, when interpreted
+   as a quantity in MODE whose signedness is indicated by UNSIGNEDP.  For
+   unsigned quantities, this is the number of high-order zero bits.
+   For signed quantities, this is the number of copies of the sign bit
+   minus 1.  In both case, this function returns the number of "spare"
+   bits.  For example, if two quantities for which this function returns
+   at least 1 are added, the addition is known not to overflow.
+
+   This function will always return 0 unless called during combine, which
+   implies that it must be called from a define_split.  */
+
+int
+extended_count (x, mode, unsignedp)
+     rtx x;
+     enum machine_mode mode;
+     int unsignedp;
+{
+  if (nonzero_sign_valid == 0)
+    return 0;
+
+  return (unsignedp
+         ? (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+            && (GET_MODE_BITSIZE (mode) - 1
+                - floor_log2 (nonzero_bits (x, mode))))
+         : num_sign_bit_copies (x, mode) - 1);
 }
 \f
 /* This function is called from `simplify_shift_const' to merge two
@@ -5648,13 +7100,33 @@ simplify_shift_const (x, code, result_mode, varop, count)
       else if (count < 0)
        abort ();
 
+      /* An arithmetic right shift of a quantity known to be -1 or 0
+        is a no-op.  */
+      if (code == ASHIFTRT
+         && (num_sign_bit_copies (varop, shift_mode)
+             == GET_MODE_BITSIZE (shift_mode)))
+       {
+         count = 0;
+         break;
+       }
+
+      /* If we are doing an arithmetic right shift and discarding all but
+        the sign bit copies, this is equivalent to doing a shift by the
+        bitsize minus one.  Convert it into that shift because it will often
+        allow other simplifications.  */
+
+      if (code == ASHIFTRT
+         && (count + num_sign_bit_copies (varop, shift_mode)
+             >= GET_MODE_BITSIZE (shift_mode)))
+       count = GET_MODE_BITSIZE (shift_mode) - 1;
+
       /* We simplify the tests below and elsewhere by converting
         ASHIFTRT to LSHIFTRT if we know the sign bit is clear.
         `make_compound_operation' will convert it to a ASHIFTRT for
         those machines (such as Vax) that don't have a LSHIFTRT.  */
       if (GET_MODE_BITSIZE (shift_mode) <= HOST_BITS_PER_WIDE_INT
          && code == ASHIFTRT
-         && ((significant_bits (varop, shift_mode)
+         && ((nonzero_bits (varop, shift_mode)
               & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (shift_mode) - 1)))
              == 0))
        code = LSHIFTRT;
@@ -5730,7 +7202,9 @@ simplify_shift_const (x, code, result_mode, varop, count)
          /* If VAROP is a SUBREG, strip it as long as the inner operand has
             the same number of words as what we've seen so far.  Then store
             the widest mode in MODE.  */
-         if (SUBREG_WORD (varop) == 0
+         if (subreg_lowpart_p (varop)
+             && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (varop)))
+                 > GET_MODE_SIZE (GET_MODE (varop)))
              && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (varop)))
                    + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD)
                  == mode_words))
@@ -5823,42 +7297,23 @@ simplify_shift_const (x, code, result_mode, varop, count)
                  continue;
                }
              
-             /* If this was (ashiftrt (ashift foo C1) C2) and we know
-                something about FOO's previous value, we may be able to
-                optimize this even though the code below can't handle this
-                case.
-
-                If FOO has J high-order bits equal to the sign bit with
-                J > C1, then we can convert this to either an ASHIFT or
-                a ASHIFTRT depending on the two counts. 
+             /* If this was (ashiftrt (ashift foo C1) C2) and FOO has more
+                than C1 high-order bits equal to the sign bit, we can convert
+                this to either an ASHIFT or a ASHIFTRT depending on the
+                two counts. 
 
                 We cannot do this if VAROP's mode is not SHIFT_MODE.  */
 
              if (code == ASHIFTRT && first_code == ASHIFT
                  && GET_MODE (varop) == shift_mode
-                 && (inner = get_last_value (XEXP (varop, 0))) != 0)
+                 && (num_sign_bit_copies (XEXP (varop, 0), shift_mode)
+                     > first_count))
                {
-                 if ((GET_CODE (inner) == CONST_INT
-                      && (((INTVAL (inner)
-                            >> (HOST_BITS_PER_WIDE_INT - (first_count + 1)))
-                           == 0)
-                          || ((INTVAL (inner)
-                               >> (HOST_BITS_PER_WIDE_INT - (first_count + 1)))
-                              == -1)))
-                     || (GET_CODE (inner) == SIGN_EXTEND
-                         && ((GET_MODE_BITSIZE (GET_MODE (inner))
-                              - GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (inner))))
-                             >= first_count))
-                     || (GET_CODE (inner) == ASHIFTRT
-                         && GET_CODE (XEXP (inner, 1)) == CONST_INT
-                         && INTVAL (XEXP (inner, 1)) >= first_count))
-                   {
-                     count -= first_count;
-                     if (count < 0)
-                       count = - count, code = ASHIFT;
-                     varop = XEXP (varop, 0);
-                     continue;
-                   }
+                 count -= first_count;
+                 if (count < 0)
+                   count = - count, code = ASHIFT;
+                 varop = XEXP (varop, 0);
+                 continue;
                }
 
              /* There are some cases we can't do.  If CODE is ASHIFTRT,
@@ -5896,10 +7351,10 @@ simplify_shift_const (x, code, result_mode, varop, count)
                break;
 
              /* To compute the mask to apply after the shift, shift the
-                significant bits of the inner shift the same way the 
+                nonzero bits of the inner shift the same way the 
                 outer shift will.  */
 
-             mask_rtx = GEN_INT (significant_bits (varop, GET_MODE (varop)));
+             mask_rtx = GEN_INT (nonzero_bits (varop, GET_MODE (varop)));
 
              mask_rtx
                = simplify_binary_operation (code, result_mode, mask_rtx,
@@ -6023,7 +7478,7 @@ simplify_shift_const (x, code, result_mode, varop, count)
          /* convert (lshift (eq FOO 0) C) to (xor FOO 1) if STORE_FLAG_VALUE
             says that the sign bit can be tested, FOO has mode MODE, C is
             GET_MODE_BITSIZE (MODE) - 1, and FOO has only the low-order bit
-            significant.  */
+            may be nonzero.  */
          if (code == LSHIFT
              && XEXP (varop, 1) == const0_rtx
              && GET_MODE (XEXP (varop, 0)) == result_mode
@@ -6031,7 +7486,7 @@ simplify_shift_const (x, code, result_mode, varop, count)
              && GET_MODE_BITSIZE (result_mode) <= HOST_BITS_PER_WIDE_INT
              && ((STORE_FLAG_VALUE
                   & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (result_mode) - 1))))
-             && significant_bits (XEXP (varop, 0), result_mode) == 1
+             && nonzero_bits (XEXP (varop, 0), result_mode) == 1
              && merge_outer_ops (&outer_op, &outer_const, XOR,
                                  (HOST_WIDE_INT) 1, result_mode,
                                  &complement_p))
@@ -6043,11 +7498,12 @@ simplify_shift_const (x, code, result_mode, varop, count)
          break;
 
        case NEG:
-         /* If we are doing an arithmetic right shift of something known
-            to be -1 or 0, we don't need the shift.  */
-         if (code == ASHIFTRT
-             && significant_bits (XEXP (varop, 0), result_mode) == 1)
+         /* (lshiftrt (neg A) C) where A is either 0 or 1 and C is one less
+            than the number of bits in the mode is equivalent to A.  */
+         if (code == LSHIFTRT && count == GET_MODE_BITSIZE (result_mode) - 1
+             && nonzero_bits (XEXP (varop, 0), result_mode) == 1)
            {
+             varop = XEXP (varop, 0);
              count = 0;
              continue;
            }
@@ -6065,20 +7521,12 @@ simplify_shift_const (x, code, result_mode, varop, count)
          break;
 
        case PLUS:
-         /* Similar to case above.  If X is 0 or 1 then X - 1 is -1 or 0.  */
-         if (XEXP (varop, 1) == constm1_rtx && code == ASHIFTRT
-             && significant_bits (XEXP (varop, 0), result_mode) == 1)
-           {
-             count = 0;
-             continue;
-           }
-
-         /* If we have the same operands as above but we are shifting the
-            sign bit into the low-order bit, we are exclusive-or'ing
-            the operand of the PLUS with a one.  */
+         /* (lshiftrt (plus A -1) C) where A is either 0 or 1 and C
+            is one less than the number of bits in the mode is
+            equivalent to (xor A 1).  */
          if (code == LSHIFTRT && count == GET_MODE_BITSIZE (result_mode) - 1
              && XEXP (varop, 1) == constm1_rtx
-             && significant_bits (XEXP (varop, 0), result_mode) == 1
+             && nonzero_bits (XEXP (varop, 0), result_mode) == 1
              && merge_outer_ops (&outer_op, &outer_const, XOR,
                                  (HOST_WIDE_INT) 1, result_mode,
                                  &complement_p))
@@ -6089,26 +7537,27 @@ simplify_shift_const (x, code, result_mode, varop, count)
            }
 
          /* If we have (xshiftrt (plus FOO BAR) C), and the only bits
-            significant in BAR are those being shifted out and those
+            that might be nonzero in BAR are those being shifted out and those
             bits are known zero in FOO, we can replace the PLUS with FOO.
             Similarly in the other operand order.  This code occurs when
             we are computing the size of a variable-size array.  */
 
          if ((code == ASHIFTRT || code == LSHIFTRT)
              && count < HOST_BITS_PER_WIDE_INT
-             && significant_bits (XEXP (varop, 1), result_mode) >> count == 0
-             && (significant_bits (XEXP (varop, 1), result_mode)
-                 & significant_bits (XEXP (varop, 0), result_mode)) == 0)
+             && nonzero_bits (XEXP (varop, 1), result_mode) >> count == 0
+             && (nonzero_bits (XEXP (varop, 1), result_mode)
+                 & nonzero_bits (XEXP (varop, 0), result_mode)) == 0)
            {
              varop = XEXP (varop, 0);
              continue;
            }
          else if ((code == ASHIFTRT || code == LSHIFTRT)
                   && count < HOST_BITS_PER_WIDE_INT
-                  && 0 == (significant_bits (XEXP (varop, 0), result_mode)
+                  && GET_MODE_BITSIZE (result_mode) <= HOST_BITS_PER_WIDE_INT
+                  && 0 == (nonzero_bits (XEXP (varop, 0), result_mode)
                            >> count)
-                  && 0 == (significant_bits (XEXP (varop, 0), result_mode)
-                           & significant_bits (XEXP (varop, 1),
+                  && 0 == (nonzero_bits (XEXP (varop, 0), result_mode)
+                           & nonzero_bits (XEXP (varop, 1),
                                                 result_mode)))
            {
              varop = XEXP (varop, 1);
@@ -6206,6 +7655,16 @@ simplify_shift_const (x, code, result_mode, varop, count)
       SUBST (XEXP (x, 1), const_rtx);
     }
 
+  /* If we have an outer operation and we just made a shift, it is
+     possible that we could have simplified the shift were it not
+     for the outer operation.  So try to do the simplification
+     recursively.  */
+
+  if (outer_op != NIL && GET_CODE (x) == code
+      && GET_CODE (XEXP (x, 1)) == CONST_INT)
+    x = simplify_shift_const (x, code, shift_mode, XEXP (x, 0),
+                             INTVAL (XEXP (x, 1)));
+
   /* If we were doing a LSHIFTRT in a wider mode than it was originally,
      turn off all the bits that the shift would have turned off.  */
   if (orig_code == LSHIFTRT && result_mode != shift_mode)
@@ -6350,7 +7809,14 @@ gen_lowpart_for_combine (mode, x)
   if (GET_MODE (x) == mode)
     return x;
 
-  if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
+  /* We can only support MODE being wider than a word if X is a
+     constant integer or has a mode the same size.  */
+
+  if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
+      && ! ((GET_MODE (x) == VOIDmode
+            && (GET_CODE (x) == CONST_INT
+                || GET_CODE (x) == CONST_DOUBLE))
+           || GET_MODE_SIZE (GET_MODE (x)) == GET_MODE_SIZE (mode)))
     return gen_rtx (CLOBBER, GET_MODE (x), const0_rtx);
 
   /* X might be a paradoxical (subreg (mem)).  In that case, gen_lowpart
@@ -6464,15 +7930,15 @@ gen_rtx_combine (va_alist)
 
   for (i = previous_num_undos; i < undobuf.num_undo; i++)
     if (!undobuf.undo[i].is_int
-       && GET_CODE (undobuf.undo[i].old_contents) == code
-       && GET_MODE (undobuf.undo[i].old_contents) == mode)
+       && GET_CODE (undobuf.undo[i].old_contents.rtx) == code
+       && GET_MODE (undobuf.undo[i].old_contents.rtx) == mode)
       {
        for (j = 0; j < n_args; j++)
-         if (XEXP (undobuf.undo[i].old_contents, j) != args[j])
+         if (XEXP (undobuf.undo[i].old_contents.rtx, j) != args[j])
            break;
 
        if (j == n_args)
-         return undobuf.undo[i].old_contents;
+         return undobuf.undo[i].old_contents.rtx;
       }
 
   /* Otherwise make a new rtx.  We know we have 1, 2, or 3 args.
@@ -6499,6 +7965,12 @@ gen_binary (code, mode, op0, op1)
      rtx op0, op1;
 {
   rtx result;
+  rtx tem;
+
+  if (GET_RTX_CLASS (code) == 'c'
+      && (GET_CODE (op0) == CONST_INT
+         || (CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT)))
+    tem = op0, op0 = op1, op1 = tem;
 
   if (GET_RTX_CLASS (code) == '<') 
     {
@@ -6568,7 +8040,7 @@ simplify_comparison (code, pop0, pop1)
     {
       /* If both operands are the same constant shift, see if we can ignore the
         shift.  We can if the shift is a rotate or if the bits shifted out of
-        this shift are not significant for either input and if the type of
+        this shift are known to be zero for both inputs and if the type of
         comparison is compatible with the shift.  */
       if (GET_CODE (op0) == GET_CODE (op1)
          && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT
@@ -6593,8 +8065,8 @@ simplify_comparison (code, pop0, pop1)
          else if (GET_CODE (op0) == ASHIFT || GET_CODE (op0) == LSHIFT)
            mask = (mask & (mask << shift_count)) >> shift_count;
 
-         if ((significant_bits (XEXP (op0, 0), mode) & ~ mask) == 0
-             && (significant_bits (XEXP (op1, 0), mode) & ~ mask) == 0)
+         if ((nonzero_bits (XEXP (op0, 0), mode) & ~ mask) == 0
+             && (nonzero_bits (XEXP (op1, 0), mode) & ~ mask) == 0)
            op0 = XEXP (op0, 0), op1 = XEXP (op1, 0);
          else
            break;
@@ -6604,9 +8076,9 @@ simplify_comparison (code, pop0, pop1)
         SUBREGs are of the same mode, and, in both cases, the AND would
         be redundant if the comparison was done in the narrower mode,
         do the comparison in the narrower mode (e.g., we are AND'ing with 1
-        and the operand's significant bits are 0xffffff01; in that case if
-        we only care about QImode, we don't need the AND).  This case occurs
-        if the output mode of an scc insn is not SImode and
+        and the operand's possibly nonzero bits are 0xffffff01; in that case
+        if we only care about QImode, we don't need the AND).  This case
+        occurs if the output mode of an scc insn is not SImode and
         STORE_FLAG_VALUE == 1 (e.g., the 386).  */
 
       else if  (GET_CODE (op0) == AND && GET_CODE (op1) == AND
@@ -6618,10 +8090,12 @@ simplify_comparison (code, pop0, pop1)
                    > GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (op0, 0)))))
                && (GET_MODE (SUBREG_REG (XEXP (op0, 0)))
                    == GET_MODE (SUBREG_REG (XEXP (op1, 0))))
-               && (significant_bits (SUBREG_REG (XEXP (op0, 0)),
+               && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (XEXP (op0, 0))))
+                   <= HOST_BITS_PER_WIDE_INT)
+               && (nonzero_bits (SUBREG_REG (XEXP (op0, 0)),
                                      GET_MODE (SUBREG_REG (XEXP (op0, 0))))
                    & ~ INTVAL (XEXP (op0, 1))) == 0
-               && (significant_bits (SUBREG_REG (XEXP (op1, 0)),
+               && (nonzero_bits (SUBREG_REG (XEXP (op1, 0)),
                                      GET_MODE (SUBREG_REG (XEXP (op1, 0))))
                    & ~ INTVAL (XEXP (op1, 1))) == 0)
        {
@@ -6677,7 +8151,7 @@ simplify_comparison (code, pop0, pop1)
        const_op &= mask;
 
       /* If we are comparing against a constant power of two and the value
-        being compared has only that single significant bit (e.g., it was
+        being compared can only have that single bit nonzero (e.g., it was
         `and'ed with that bit), we can replace this with a comparison
         with zero.  */
       if (const_op
@@ -6685,12 +8159,24 @@ simplify_comparison (code, pop0, pop1)
              || code == LT || code == LTU)
          && mode_width <= HOST_BITS_PER_WIDE_INT
          && exact_log2 (const_op) >= 0
-         && significant_bits (op0, mode) == const_op)
+         && nonzero_bits (op0, mode) == const_op)
        {
          code = (code == EQ || code == GE || code == GEU ? NE : EQ);
          op1 = const0_rtx, const_op = 0;
        }
 
+      /* Similarly, if we are comparing a value known to be either -1 or
+        0 with -1, change it to the opposite comparison against zero.  */
+
+      if (const_op == -1
+         && (code == EQ || code == NE || code == GT || code == LE
+             || code == GEU || code == LTU)
+         && num_sign_bit_copies (op0, mode) == mode_width)
+       {
+         code = (code == EQ || code == LE || code == GEU ? NE : EQ);
+         op1 = const0_rtx, const_op = 0;
+       }
+
       /* Do some canonicalizations based on the comparison code.  We prefer
         comparisons against zero and then prefer equality comparisons.  
         If we can reduce the size of a constant, we will do that too.  */
@@ -6722,7 +8208,7 @@ simplify_comparison (code, pop0, pop1)
             a zero sign bit, we can replace this with == 0.  */
          else if (const_op == 0
                   && mode_width <= HOST_BITS_PER_WIDE_INT
-                  && (significant_bits (op0, mode)
+                  && (nonzero_bits (op0, mode)
                       & ((HOST_WIDE_INT) 1 << (mode_width - 1))) == 0)
            code = EQ;
          break;
@@ -6752,7 +8238,7 @@ simplify_comparison (code, pop0, pop1)
             a zero sign bit, we can replace this with != 0.  */
          else if (const_op == 0
                   && mode_width <= HOST_BITS_PER_WIDE_INT
-                  && (significant_bits (op0, mode)
+                  && (nonzero_bits (op0, mode)
                       & ((HOST_WIDE_INT) 1 << (mode_width - 1))) == 0)
            code = NE;
          break;
@@ -6766,6 +8252,14 @@ simplify_comparison (code, pop0, pop1)
              code = LEU;
              /* ... fall through ... */
            }
+
+         /* (unsigned) < 0x80000000 is equivalent to >= 0.  */
+         else if (const_op == (HOST_WIDE_INT) 1 << (mode_width - 1))
+           {
+             const_op = 0, op1 = const0_rtx;
+             code = GE;
+             break;
+           }
          else
            break;
 
@@ -6773,6 +8267,13 @@ simplify_comparison (code, pop0, pop1)
          /* unsigned <= 0 is equivalent to == 0 */
          if (const_op == 0)
            code = EQ;
+
+         /* (unsigned) <= 0x7fffffff is equivalent to >= 0. */
+         else if (const_op == ((HOST_WIDE_INT) 1 << (mode_width - 1)) - 1)
+           {
+             const_op = 0, op1 = const0_rtx;
+             code = GE;
+           }
          break;
 
        case GEU:
@@ -6784,6 +8285,13 @@ simplify_comparison (code, pop0, pop1)
              code = GTU;
              /* ... fall through ... */
            }
+
+         /* (unsigned) >= 0x80000000 is equivalent to < 0.  */
+         else if (const_op == (HOST_WIDE_INT) 1 << (mode_width - 1))
+           {
+             const_op = 0, op1 = const0_rtx;
+             code = LT;
+           }
          else
            break;
 
@@ -6791,6 +8299,13 @@ simplify_comparison (code, pop0, pop1)
          /* unsigned > 0 is equivalent to != 0 */
          if (const_op == 0)
            code = NE;
+
+         /* (unsigned) > 0x7fffffff is equivalent to < 0.  */
+         else if (const_op == ((HOST_WIDE_INT) 1 << (mode_width - 1)) - 1)
+           {
+             const_op = 0, op1 = const0_rtx;
+             code = LT;
+           }
          break;
        }
 
@@ -6887,7 +8402,7 @@ simplify_comparison (code, pop0, pop1)
          if (sign_bit_comparison_p
              && (GET_CODE (XEXP (op0, 0)) == ABS
                  || (mode_width <= HOST_BITS_PER_WIDE_INT
-                     && (significant_bits (XEXP (op0, 0), mode)
+                     && (nonzero_bits (XEXP (op0, 0), mode)
                          & ((HOST_WIDE_INT) 1 << (mode_width - 1))) == 0)))
            {
              op0 = XEXP (op0, 0);
@@ -6895,28 +8410,9 @@ simplify_comparison (code, pop0, pop1)
              continue;
            }
 
-         /* If we have NEG of something that is the result of a
-            SIGN_EXTEND, SIGN_EXTRACT, or ASHIFTRT, we know that the
-            two high-order bits must be the same and hence that
-            "(-a) < 0" is equivalent to "a > 0".  Otherwise, we can't
-            do this.  */
-         if (GET_CODE (XEXP (op0, 0)) == SIGN_EXTEND
-             || (GET_CODE (XEXP (op0, 0)) == SIGN_EXTRACT
-                 && GET_CODE (XEXP (XEXP (op0, 0), 1)) == CONST_INT
-                 && (INTVAL (XEXP (XEXP (op0, 0), 1))
-                     < GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (op0, 0), 0)))))
-             || (GET_CODE (XEXP (op0, 0)) == ASHIFTRT
-                 && GET_CODE (XEXP (XEXP (op0, 0), 1)) == CONST_INT
-                 && XEXP (XEXP (op0, 0), 1) != const0_rtx)
-             || ((tem = get_last_value (XEXP (op0, 0))) != 0
-                 && (GET_CODE (tem) == SIGN_EXTEND
-                     || (GET_CODE (tem) == SIGN_EXTRACT
-                         && GET_CODE (XEXP (tem, 1)) == CONST_INT
-                         && (INTVAL (XEXP (tem, 1))
-                             < GET_MODE_BITSIZE (GET_MODE (XEXP (tem, 0)))))
-                     || (GET_CODE (tem) == ASHIFTRT
-                         && GET_CODE (XEXP (tem, 1)) == CONST_INT
-                         && XEXP (tem, 1) != const0_rtx))))
+         /* If we have NEG of something whose two high-order bits are the
+            same, we know that "(-a) < 0" is equivalent to "a > 0". */
+         if (num_sign_bit_copies (op0, mode) >= 2)
            {
              op0 = XEXP (op0, 0);
              code = swap_condition (code);
@@ -6981,10 +8477,41 @@ simplify_comparison (code, pop0, pop1)
          break;
 
        case SUBREG:
-         /* If the inner mode is smaller and we are extracting the low
-            part, we can treat the SUBREG as if it were a ZERO_EXTEND.  */
-         if (! subreg_lowpart_p (op0)
-             || GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0))) >= mode_width)
+         /* Check for the case where we are comparing A - C1 with C2,
+            both constants are smaller than 1/2 the maxium positive
+            value in MODE, and the comparison is equality or unsigned.
+            In that case, if A is either zero-extended to MODE or has
+            sufficient sign bits so that the high-order bit in MODE
+            is a copy of the sign in the inner mode, we can prove that it is
+            safe to do the operation in the wider mode.  This simplifies
+            many range checks.  */
+
+         if (mode_width <= HOST_BITS_PER_WIDE_INT
+             && subreg_lowpart_p (op0)
+             && GET_CODE (SUBREG_REG (op0)) == PLUS
+             && GET_CODE (XEXP (SUBREG_REG (op0), 1)) == CONST_INT
+             && INTVAL (XEXP (SUBREG_REG (op0), 1)) < 0
+             && (- INTVAL (XEXP (SUBREG_REG (op0), 1))
+                 < GET_MODE_MASK (mode) / 2)
+             && (unsigned HOST_WIDE_INT) const_op < GET_MODE_MASK (mode) / 2
+             && (0 == (nonzero_bits (XEXP (SUBREG_REG (op0), 0),
+                                     GET_MODE (SUBREG_REG (op0)))
+                       & ~ GET_MODE_MASK (mode))
+                 || (num_sign_bit_copies (XEXP (SUBREG_REG (op0), 0),
+                                          GET_MODE (SUBREG_REG (op0)))
+                     > (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))
+                        - GET_MODE_BITSIZE (mode)))))
+           {
+             op0 = SUBREG_REG (op0);
+             continue;
+           }
+
+         /* If the inner mode is narrower and we are extracting the low part,
+            we can treat the SUBREG as if it were a ZERO_EXTEND.  */
+         if (subreg_lowpart_p (op0)
+             && GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0))) < mode_width)
+           /* Fall through */ ;
+         else
            break;
 
          /* ... fall through ... */
@@ -7002,12 +8529,12 @@ simplify_comparison (code, pop0, pop1)
          break;
 
        case PLUS:
-         /* (eq (plus X C1) C2) -> (eq X (minus C2 C1)).  We can only do
+         /* (eq (plus X A) B) -> (eq X (minus B A)).  We can only do
             this for equality comparisons due to pathological cases involving
             overflows.  */
-         if (equality_comparison_p && GET_CODE (XEXP (op0, 1)) == CONST_INT
-             && (tem = simplify_binary_operation (MINUS, mode, op1,
-                                                  XEXP (op0, 1))) != 0)
+         if (equality_comparison_p
+             && 0 != (tem = simplify_binary_operation (MINUS, mode,
+                                                       op1, XEXP (op0, 1))))
            {
              op0 = XEXP (op0, 0);
              op1 = tem;
@@ -7025,6 +8552,28 @@ simplify_comparison (code, pop0, pop1)
          break;
 
        case MINUS:
+         /* (eq (minus A B) C) -> (eq A (plus B C)) or
+            (eq B (minus A C)), whichever simplifies.  We can only do
+            this for equality comparisons due to pathological cases involving
+            overflows.  */
+         if (equality_comparison_p
+             && 0 != (tem = simplify_binary_operation (PLUS, mode,
+                                                       XEXP (op0, 1), op1)))
+           {
+             op0 = XEXP (op0, 0);
+             op1 = tem;
+             continue;
+           }
+
+         if (equality_comparison_p
+             && 0 != (tem = simplify_binary_operation (MINUS, mode,
+                                                       XEXP (op0, 0), op1)))
+           {
+             op0 = XEXP (op0, 1);
+             op1 = tem;
+             continue;
+           }
+
          /* The sign bit of (minus (ashiftrt X C) X), where C is the number
             of bits in X minus 1, is one iff X > 0.  */
          if (sign_bit_comparison_p && GET_CODE (XEXP (op0, 0)) == ASHIFTRT
@@ -7176,7 +8725,7 @@ simplify_comparison (code, pop0, pop1)
        case LSHIFT:
          /* If we have (compare (xshift FOO N) (const_int C)) and
             the high order N bits of FOO (N+1 if an inequality comparison)
-            are not significant, we can do this by comparing FOO with C
+            are known to be zero, we can do this by comparing FOO with C
             shifted right N bits so long as the low-order N bits of C are
             zero.  */
          if (GET_CODE (XEXP (op0, 1)) == CONST_INT
@@ -7184,10 +8733,9 @@ simplify_comparison (code, pop0, pop1)
              && ((INTVAL (XEXP (op0, 1)) + ! equality_comparison_p)
                  < HOST_BITS_PER_WIDE_INT)
              && ((const_op
-                  &  ~ (((HOST_WIDE_INT) 1
-                         << INTVAL (XEXP (op0, 1))) - 1)) == 0)
+                  & (((HOST_WIDE_INT) 1 << INTVAL (XEXP (op0, 1))) - 1)) == 0)
              && mode_width <= HOST_BITS_PER_WIDE_INT
-             && (significant_bits (XEXP (op0, 0), mode)
+             && (nonzero_bits (XEXP (op0, 0), mode)
                  & ~ (mask >> (INTVAL (XEXP (op0, 1))
                                + ! equality_comparison_p))) == 0)
            {
@@ -7224,6 +8772,17 @@ simplify_comparison (code, pop0, pop1)
          break;
 
        case ASHIFTRT:
+         /* If this is an equality comparison with zero, we can do this
+            as a logical shift, which might be much simpler.  */
+         if (equality_comparison_p && const_op == 0
+             && GET_CODE (XEXP (op0, 1)) == CONST_INT)
+           {
+             op0 = simplify_shift_const (NULL_RTX, LSHIFTRT, mode,
+                                         XEXP (op0, 0),
+                                         INTVAL (XEXP (op0, 1)));
+             continue;
+           }
+
          /* If OP0 is a sign extension and CODE is not an unsigned comparison,
             do the comparison in a narrower mode.  */
          if (! unsigned_comparison_p
@@ -7231,7 +8790,7 @@ simplify_comparison (code, pop0, pop1)
              && GET_CODE (XEXP (op0, 0)) == ASHIFT
              && XEXP (op0, 1) == XEXP (XEXP (op0, 0), 1)
              && (tmode = mode_for_size (mode_width - INTVAL (XEXP (op0, 1)),
-                                        MODE_INT, 1)) != VOIDmode
+                                        MODE_INT, 1)) != BLKmode
              && ((unsigned HOST_WIDE_INT) const_op <= GET_MODE_MASK (tmode)
                  || ((unsigned HOST_WIDE_INT) - const_op
                      <= GET_MODE_MASK (tmode))))
@@ -7243,14 +8802,14 @@ simplify_comparison (code, pop0, pop1)
          /* ... fall through ... */
        case LSHIFTRT:
          /* If we have (compare (xshiftrt FOO N) (const_int C)) and
-            the low order N bits of FOO are not significant, we can do this
+            the low order N bits of FOO are known to be zero, we can do this
             by comparing FOO with C shifted left N bits so long as no
             overflow occurs.  */
          if (GET_CODE (XEXP (op0, 1)) == CONST_INT
              && INTVAL (XEXP (op0, 1)) >= 0
              && INTVAL (XEXP (op0, 1)) < HOST_BITS_PER_WIDE_INT
              && mode_width <= HOST_BITS_PER_WIDE_INT
-             && (significant_bits (XEXP (op0, 0), mode)
+             && (nonzero_bits (XEXP (op0, 0), mode)
                  & (((HOST_WIDE_INT) 1 << INTVAL (XEXP (op0, 1))) - 1)) == 0
              && (const_op == 0
                  || (floor_log2 (const_op) + INTVAL (XEXP (op0, 1))
@@ -7302,23 +8861,21 @@ simplify_comparison (code, pop0, pop1)
   else if (GET_CODE (op0) == SUBREG && subreg_lowpart_p (op0)
           && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT
           && (code == NE || code == EQ)
-          && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT
-          && (significant_bits (SUBREG_REG (op0), GET_MODE (SUBREG_REG (op0)))
+          && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))
+              <= HOST_BITS_PER_WIDE_INT)
+          && (nonzero_bits (SUBREG_REG (op0), GET_MODE (SUBREG_REG (op0)))
               & ~ GET_MODE_MASK (GET_MODE (op0))) == 0
           && (tem = gen_lowpart_for_combine (GET_MODE (SUBREG_REG (op0)),
                                              op1),
-              (significant_bits (tem, GET_MODE (SUBREG_REG (op0)))
+              (nonzero_bits (tem, GET_MODE (SUBREG_REG (op0)))
                & ~ GET_MODE_MASK (GET_MODE (op0))) == 0))
     op0 = SUBREG_REG (op0), op1 = tem;
 
   /* We now do the opposite procedure: Some machines don't have compare
      insns in all modes.  If OP0's mode is an integer mode smaller than a
      word and we can't do a compare in that mode, see if there is a larger
-     mode for which we can do the compare and where the only significant
-     bits in OP0 and OP1 are those in the narrower mode.  We can do
-     this if this is an equality comparison, in which case we can
-     merely widen the operation, or if we are testing the sign bit, in
-     which case we can explicitly put in the test.  */
+     mode for which we can do the compare.  There are a number of cases in
+     which we can use the wider mode.  */
 
   mode = GET_MODE (op0);
   if (mode != VOIDmode && GET_MODE_CLASS (mode) == MODE_INT
@@ -7328,25 +8885,41 @@ simplify_comparison (code, pop0, pop1)
         (tmode != VOIDmode
          && GET_MODE_BITSIZE (tmode) <= HOST_BITS_PER_WIDE_INT);
         tmode = GET_MODE_WIDER_MODE (tmode))
-      if (cmp_optab->handlers[(int) tmode].insn_code != CODE_FOR_nothing
-         && (significant_bits (op0, tmode) & ~ GET_MODE_MASK (mode)) == 0
-         && (significant_bits (op1, tmode) & ~ GET_MODE_MASK (mode)) == 0
-         && (code == EQ || code == NE
-             || (op1 == const0_rtx && (code == LT || code == GE)
-                 && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)))
+      if (cmp_optab->handlers[(int) tmode].insn_code != CODE_FOR_nothing)
        {
-         op0 = gen_lowpart_for_combine (tmode, op0);
-         op1 = gen_lowpart_for_combine (tmode, op1);
+         /* If the only nonzero bits in OP0 and OP1 are those in the
+            narrower mode and this is an equality or unsigned comparison,
+            we can use the wider mode.  Similarly for sign-extended
+            values and equality or signed comparisons.  */
+         if (((code == EQ || code == NE
+               || code == GEU || code == GTU || code == LEU || code == LTU)
+              && (nonzero_bits (op0, tmode) & ~ GET_MODE_MASK (mode)) == 0
+              && (nonzero_bits (op1, tmode) & ~ GET_MODE_MASK (mode)) == 0)
+             || ((code == EQ || code == NE
+                  || code == GE || code == GT || code == LE || code == LT)
+                 && (num_sign_bit_copies (op0, tmode)
+                     > GET_MODE_BITSIZE (tmode) - GET_MODE_BITSIZE (mode))
+                 && (num_sign_bit_copies (op1, tmode)
+                     > GET_MODE_BITSIZE (tmode) - GET_MODE_BITSIZE (mode))))
+           {
+             op0 = gen_lowpart_for_combine (tmode, op0);
+             op1 = gen_lowpart_for_combine (tmode, op1);
+             break;
+           }
 
-         if (code == LT || code == GE)
+         /* If this is a test for negative, we can make an explicit
+            test of the sign bit.  */
+
+         if (op1 == const0_rtx && (code == LT || code == GE)
+             && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
            {
-             op0 = gen_binary (AND, tmode, op0,
+             op0 = gen_binary (AND, tmode,
+                               gen_lowpart_for_combine (tmode, op0),
                                GEN_INT ((HOST_WIDE_INT) 1
                                         << (GET_MODE_BITSIZE (mode) - 1)));
              code = (code == LT) ? NE : EQ;
+             break;
            }
-
-         break;
        }
 
   *pop0 = op0;
@@ -7370,12 +8943,14 @@ reversible_comparison_p (x)
   switch (GET_MODE_CLASS (GET_MODE (XEXP (x, 0))))
     {
     case MODE_INT:
+    case MODE_PARTIAL_INT:
+    case MODE_COMPLEX_INT:
       return 1;
 
     case MODE_CC:
       x = get_last_value (XEXP (x, 0));
       return (x && GET_CODE (x) == COMPARE
-             && GET_MODE_CLASS (GET_MODE (XEXP (x, 0))) == MODE_INT);
+             && ! FLOAT_MODE_P (GET_MODE (XEXP (x, 0))));
     }
 
   return 0;
@@ -7414,8 +8989,8 @@ update_table_tick (x)
 
 /* Record that REG is set to VALUE in insn INSN.  If VALUE is zero, we
    are saying that the register is clobbered and we no longer know its
-   value.  If INSN is zero, don't update reg_last_set; this call is normally
-   done with VALUE also zero to invalidate the register.  */
+   value.  If INSN is zero, don't update reg_last_set; this is only permitted
+   with VALUE also zero and is used to invalidate the register.  */
 
 static void
 record_value_for_reg (reg, insn, value)
@@ -7485,9 +9060,19 @@ record_value_for_reg (reg, insn, value)
        value = 0;
     }
 
-  /* For the main register being modified, update the value.  */
+  /* For the main register being modified, update the value, the mode, the
+     nonzero bits, and the number of sign bit copies.  */
+
   reg_last_set_value[regno] = value;
 
+  if (value)
+    {
+      subst_low_cuid = INSN_CUID (insn);
+      reg_last_set_mode[regno] = GET_MODE (reg);
+      reg_last_set_nonzero_bits[regno] = nonzero_bits (value, GET_MODE (reg));
+      reg_last_set_sign_bit_copies[regno]
+       = num_sign_bit_copies (value, GET_MODE (reg));
+    }
 }
 
 /* Used for communication between the following two routines.  */
@@ -7511,10 +9096,9 @@ record_dead_and_set_regs_1 (dest, setter)
               && GET_CODE (SET_DEST (setter)) == SUBREG
               && SUBREG_REG (SET_DEST (setter)) == dest
               && subreg_lowpart_p (SET_DEST (setter)))
-       record_value_for_reg
-         (dest, record_dead_insn,
-          gen_lowpart_for_combine (GET_MODE (SET_DEST (setter)),
-                                   SET_SRC (setter)));
+       record_value_for_reg (dest, record_dead_insn,
+                             gen_lowpart_for_combine (GET_MODE (dest),
+                                                      SET_SRC (setter)));
       else
        record_value_for_reg (dest, record_dead_insn, NULL_RTX);
     }
@@ -7537,16 +9121,37 @@ record_dead_and_set_regs (insn)
      rtx insn;
 {
   register rtx link;
+  int i;
+
   for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
     {
-      if (REG_NOTE_KIND (link) == REG_DEAD)
-       reg_last_death[REGNO (XEXP (link, 0))] = insn;
+      if (REG_NOTE_KIND (link) == REG_DEAD
+         && GET_CODE (XEXP (link, 0)) == REG)
+       {
+         int regno = REGNO (XEXP (link, 0));
+         int endregno
+           = regno + (regno < FIRST_PSEUDO_REGISTER
+                      ? HARD_REGNO_NREGS (regno, GET_MODE (XEXP (link, 0)))
+                      : 1);
+
+         for (i = regno; i < endregno; i++)
+           reg_last_death[i] = insn;
+       }
       else if (REG_NOTE_KIND (link) == REG_INC)
        record_value_for_reg (XEXP (link, 0), insn, NULL_RTX);
     }
 
   if (GET_CODE (insn) == CALL_INSN)
-    last_call_cuid = mem_last_set = INSN_CUID (insn);
+    {
+      for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+       if (call_used_regs[i])
+         {
+           reg_last_set_value[i] = 0;
+           reg_last_death[i] = 0;
+         }
+
+      last_call_cuid = mem_last_set = INSN_CUID (insn);
+    }
 
   record_dead_insn = insn;
   note_stores (PATTERN (insn), record_dead_and_set_regs_1);
@@ -7633,16 +9238,49 @@ get_last_value (x)
   regno = REGNO (x);
   value = reg_last_set_value[regno];
 
-  /* If we don't have a value, it isn't for this basic block, or if it was
-     set in a later insn that the ones we are processing, return 0.  */
+  /* If we don't have a value or if it isn't for this basic block, return 0. */
 
   if (value == 0
       || (reg_n_sets[regno] != 1
-         && (reg_last_set_label[regno] != label_tick
-             || INSN_CUID (reg_last_set[regno]) >= subst_low_cuid)))
+         && reg_last_set_label[regno] != label_tick))
     return 0;
 
-  /* If the value has all its register valid, return it.  */
+  /* If the value was set in a later insn that the ones we are processing,
+     we can't use it even if the register was only set once, but make a quick
+     check to see if the previous insn set it to something.  This is commonly
+     the case when the same pseudo is used by repeated insns.  */
+
+  if (INSN_CUID (reg_last_set[regno]) >= subst_low_cuid)
+    {
+      rtx insn, set;
+
+      for (insn = prev_nonnote_insn (subst_insn);
+          insn && INSN_CUID (insn) >= subst_low_cuid;
+          insn = prev_nonnote_insn (insn))
+       ;
+
+      if (insn
+         && (set = single_set (insn)) != 0
+         && rtx_equal_p (SET_DEST (set), x))
+       {
+         value = SET_SRC (set);
+
+         /* Make sure that VALUE doesn't reference X.  Replace any
+            expliit references with a CLOBBER.  If there are any remaining
+            references (rare), don't use the value.  */
+
+         if (reg_mentioned_p (x, value))
+           value = replace_rtx (copy_rtx (value), x,
+                                gen_rtx (CLOBBER, GET_MODE (x), const0_rtx));
+
+         if (reg_overlap_mentioned_p (x, value))
+           return 0;
+       }
+      else
+       return 0;
+    }
+
+  /* If the value has all its registers valid, return it.  */
   if (get_last_value_validate (&value, reg_last_set_label[regno], 0))
     return value;
 
@@ -7800,7 +9438,10 @@ remove_death (regno, insn)
   register rtx note = find_regno_note (insn, REG_DEAD, regno);
 
   if (note)
-    remove_note (insn, note);
+    {
+      reg_n_deaths[regno]--;
+      remove_note (insn, note);
+    }
 
   return note;
 }
@@ -7832,19 +9473,45 @@ move_deaths (x, from_cuid, to_insn, pnotes)
       if (where_dead && INSN_CUID (where_dead) >= from_cuid
          && INSN_CUID (where_dead) < INSN_CUID (to_insn))
        {
-         rtx note = remove_death (regno, reg_last_death[regno]);
+         rtx note = remove_death (regno, where_dead);
 
          /* It is possible for the call above to return 0.  This can occur
             when reg_last_death points to I2 or I1 that we combined with.
-            In that case make a new note.  */
+            In that case make a new note.
+
+            We must also check for the case where X is a hard register
+            and NOTE is a death note for a range of hard registers
+            including X.  In that case, we must put REG_DEAD notes for
+            the remaining registers in place of NOTE.  */
+
+         if (note != 0 && regno < FIRST_PSEUDO_REGISTER
+             && (GET_MODE_SIZE (GET_MODE (XEXP (note, 0)))
+                 != GET_MODE_SIZE (GET_MODE (x))))
+           {
+             int deadregno = REGNO (XEXP (note, 0));
+             int deadend
+               = (deadregno + HARD_REGNO_NREGS (deadregno,
+                                                GET_MODE (XEXP (note, 0))));
+             int ourend = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
+             int i;
+
+             for (i = deadregno; i < deadend; i++)
+               if (i < regno || i >= ourend)
+                 REG_NOTES (where_dead)
+                   = gen_rtx (EXPR_LIST, REG_DEAD,
+                              gen_rtx (REG, word_mode, i),
+                              REG_NOTES (where_dead));
+           }
 
-         if (note)
+         if (note != 0 && GET_MODE (XEXP (note, 0)) == GET_MODE (x))
            {
              XEXP (note, 1) = *pnotes;
              *pnotes = note;
            }
          else
            *pnotes = gen_rtx (EXPR_LIST, REG_DEAD, x, *pnotes);
+
+         reg_n_deaths[regno]++;
        }
 
       return;
@@ -8015,8 +9682,17 @@ distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1)
        case REG_NONNEG:
          /* These notes say something about results of an insn.  We can
             only support them if they used to be on I3 in which case they
-            remain on I3.  Otherwise they are ignored.  */
-         if (from_insn == i3)
+            remain on I3.  Otherwise they are ignored.
+
+            If the note refers to an expression that is not a constant, we
+            must also ignore the note since we cannot tell whether the
+            equivalence is still true.  It might be possible to do
+            slightly better than this (we only have a problem if I2DEST
+            or I1DEST is present in the expression), but it doesn't
+            seem worth the trouble.  */
+
+         if (from_insn == i3
+             && (XEXP (note, 0) == 0 || CONSTANT_P (XEXP (note, 0))))
            place = i3;
          break;
 
@@ -8094,6 +9770,18 @@ distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1)
          if (XEXP (note, 0) == elim_i2 || XEXP (note, 0) == elim_i1)
            break;
 
+         /* If the register is used in both I2 and I3 and it dies in I3, 
+            we might have added another reference to it.  If reg_n_refs
+            was 2, bump it to 3.  This has to be correct since the 
+            register must have been set somewhere.  The reason this is
+            done is because local-alloc.c treats 2 references as a 
+            special case.  */
+
+         if (place == i3 && i2 != 0 && GET_CODE (XEXP (note, 0)) == REG
+             && reg_n_refs[REGNO (XEXP (note, 0))]== 2
+             && reg_referenced_p (XEXP (note, 0), PATTERN (i2)))
+           reg_n_refs[REGNO (XEXP (note, 0))] = 3;
+
          if (place == 0)
            for (tem = prev_nonnote_insn (i3);
                 tem && (GET_CODE (tem) == INSN
@@ -8225,6 +9913,16 @@ distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1)
                        all_used = 0;
                      }
 
+                 /* Check for the case where the register dying partially
+                    overlaps the register set by this insn.  */
+                 if (all_used)
+                   for (i = regno; i < endregno; i++)
+                     if (dead_or_set_regno_p (place, i))
+                         {
+                           all_used = 0;
+                           break;
+                         }
+
                  if (! all_used)
                    {
                      /* Put only REG_DEAD notes for pieces that are
@@ -8260,10 +9958,21 @@ distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1)
          XEXP (note, 1) = REG_NOTES (place);
          REG_NOTES (place) = note;
        }
+      else if ((REG_NOTE_KIND (note) == REG_DEAD
+               || REG_NOTE_KIND (note) == REG_UNUSED)
+              && GET_CODE (XEXP (note, 0)) == REG)
+       reg_n_deaths[REGNO (XEXP (note, 0))]--;
 
       if (place2)
-       REG_NOTES (place2) = gen_rtx (GET_CODE (note), REG_NOTE_KIND (note),
-                                     XEXP (note, 0), REG_NOTES (place2));
+       {
+         if ((REG_NOTE_KIND (note) == REG_DEAD
+              || REG_NOTE_KIND (note) == REG_UNUSED)
+             && GET_CODE (XEXP (note, 0)) == REG)
+           reg_n_deaths[REGNO (XEXP (note, 0))]++;
+
+         REG_NOTES (place2) = gen_rtx (GET_CODE (note), REG_NOTE_KIND (note),
+                                       XEXP (note, 0), REG_NOTES (place2));
+       }
     }
 }
 \f