calls.c (expand_call): Only destroy temporaries at the end of function calls, if...
[platform/upstream/gcc.git] / gcc / combine.c
index a33bcc1..c9a0b3b 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, 1994 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -74,7 +74,15 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
    combine anyway.  */
 
 #include "config.h"
-#include "gvarargs.h"
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+/* Must precede rtl.h for FFS.  */
+#include <stdio.h>
+
 #include "rtl.h"
 #include "flags.h"
 #include "regs.h"
@@ -87,26 +95,11 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "insn-attr.h"
 #include "recog.h"
 #include "real.h"
-#include <stdio.h>
 
 /* It is not safe to use ordinary gen_lowpart in combine.
    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;
@@ -126,6 +119,13 @@ static int combine_successes;
 /* Totals over entire compilation.  */
 
 static int total_attempts, total_merges, total_extras, total_successes;
+
+/* Define a defulat value for REVERSIBLE_CC_MODE.
+   We can never assume that a condition code mode is safe to reverse unless
+   the md tells us so.  */
+#ifndef REVERSIBLE_CC_MODE
+#define REVERSIBLE_CC_MODE(MODE) 0
+#endif
 \f
 /* Vector mapping INSN_UIDs to cuids.
    The cuids are like uids but increase monotonically always.
@@ -170,6 +170,11 @@ static int last_call_cuid;
 
 static rtx subst_insn;
 
+/* If nonzero, this is the insn that should be presumed to be
+   immediately in front of `subst_insn'.  */
+
+static rtx subst_prev_insn;
+
 /* This is the lowest CUID that `subst' is currently dealing with.
    get_last_value will not return a value if the register was set at or
    after this CUID.  If not for this mechanism, we could get confused if
@@ -179,12 +184,26 @@ static rtx subst_insn;
 
 static int subst_low_cuid;
 
+/* This contains any hard registers that are used in newpat; reg_dead_at_p
+   must consider all these registers to be always live.  */
+
+static HARD_REG_SET newpat_used_regs;
+
+/* This is an insn to which a LOG_LINKS entry has been added.  If this
+   insn is the earlier than I2 or I3, combine should rescan starting at
+   that location.  */
+
+static rtx added_links_insn;
+
 /* This is the value of undobuf.num_undo when we started processing this 
    substitution.  This will prevent gen_rtx_combine from re-used a piece
    from the previous expression.  Doing so can produce circular rtl
    structures.  */
 
 static int previous_num_undos;
+
+/* Basic block number of the block in which we are performing combines.  */
+static int this_basic_block;
 \f
 /* The next group of arrays allows the recording of the last value assigned
    to (hard or pseudo) register n.  We use this information to see if a
@@ -237,12 +256,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.  */
@@ -251,7 +270,7 @@ 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,
@@ -298,8 +317,8 @@ static char *reg_last_set_sign_bit_copies;
 struct undo
 {
   int is_int;
-  union {rtx rtx; int i;} old_contents;
-  union {rtx *rtx; int *i;} where;
+  union {rtx r; int i;} old_contents;
+  union {rtx *r; int *i;} where;
 };
 
 /* Record a bunch of changes to be undone, up to MAX_UNDO of them.
@@ -334,10 +353,10 @@ static struct undobuf undobuf;
       if (undobuf.num_undo < MAX_UNDO)                                 \
        {                                                               \
          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;       \
+         undobuf.undo[undobuf.num_undo].where.r = &INTO;               \
+         undobuf.undo[undobuf.num_undo].old_contents.r = INTO; \
          INTO = _new;                                                  \
-         if (undobuf.undo[undobuf.num_undo].old_contents.rtx != INTO)  \
+         if (undobuf.undo[undobuf.num_undo].old_contents.r != INTO)    \
            undobuf.num_undo++;                                         \
        }                                                               \
     } while (0)
@@ -363,43 +382,64 @@ static struct undobuf undobuf;
 
 static int n_occurrences;
 
-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 ();
-static void record_dead_and_set_regs ();
-static int use_crosses_set_p ();
-static rtx try_combine ();
-static rtx *find_split_point ();
-static rtx subst ();
-static void undo_all ();
-static int reg_dead_at_p ();
-static rtx expand_compound_operation ();
-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 nonzero_bits ();
-static int num_sign_bit_copies ();
-static int merge_outer_ops ();
-static rtx simplify_shift_const ();
-static int recog_for_combine ();
-static rtx gen_lowpart_for_combine ();
-static rtx gen_rtx_combine ();
-static rtx gen_binary ();
-static rtx gen_unary ();
-static enum rtx_code simplify_comparison ();
-static int reversible_comparison_p ();
-static int get_last_value_validate ();
-static rtx get_last_value ();
-static void distribute_notes ();
-static void distribute_links ();
+static void init_reg_last_arrays       PROTO(());
+static void setup_incoming_promotions   PROTO(());
+static void set_nonzero_bits_and_sign_copies  PROTO((rtx, rtx));
+static int can_combine_p       PROTO((rtx, rtx, rtx, rtx, rtx *, rtx *));
+static int combinable_i3pat    PROTO((rtx, rtx *, rtx, rtx, int, rtx *));
+static rtx try_combine         PROTO((rtx, rtx, rtx));
+static void undo_all           PROTO((void));
+static rtx *find_split_point   PROTO((rtx *, rtx));
+static rtx subst               PROTO((rtx, rtx, rtx, int, int));
+static rtx simplify_rtx                PROTO((rtx, enum machine_mode, int, int));
+static rtx simplify_if_then_else  PROTO((rtx));
+static rtx simplify_set                PROTO((rtx));
+static rtx simplify_logical    PROTO((rtx, int));
+static rtx expand_compound_operation  PROTO((rtx));
+static rtx expand_field_assignment  PROTO((rtx));
+static rtx make_extraction     PROTO((enum machine_mode, rtx, int, rtx, int,
+                                      int, int, int));
+static rtx extract_left_shift  PROTO((rtx, int));
+static rtx make_compound_operation  PROTO((rtx, enum rtx_code));
+static int get_pos_from_mask   PROTO((unsigned HOST_WIDE_INT, int *));
+static rtx force_to_mode       PROTO((rtx, enum machine_mode,
+                                      unsigned HOST_WIDE_INT, rtx, int));
+static rtx if_then_else_cond   PROTO((rtx, rtx *, rtx *));
+static rtx known_cond          PROTO((rtx, enum rtx_code, rtx, rtx));
+static rtx make_field_assignment  PROTO((rtx));
+static rtx apply_distributive_law  PROTO((rtx));
+static rtx simplify_and_const_int  PROTO((rtx, enum machine_mode, rtx,
+                                         unsigned HOST_WIDE_INT));
+static unsigned HOST_WIDE_INT nonzero_bits  PROTO((rtx, enum machine_mode));
+static int num_sign_bit_copies  PROTO((rtx, enum machine_mode));
+static int merge_outer_ops     PROTO((enum rtx_code *, HOST_WIDE_INT *,
+                                      enum rtx_code, HOST_WIDE_INT,
+                                      enum machine_mode, int *));
+static rtx simplify_shift_const        PROTO((rtx, enum rtx_code, enum machine_mode,
+                                      rtx, int));
+static int recog_for_combine   PROTO((rtx *, rtx, rtx *));
+static rtx gen_lowpart_for_combine  PROTO((enum machine_mode, rtx));
+static rtx gen_rtx_combine PVPROTO((enum rtx_code code, enum machine_mode mode,
+                                 ...));
+static rtx gen_binary          PROTO((enum rtx_code, enum machine_mode,
+                                      rtx, rtx));
+static rtx gen_unary           PROTO((enum rtx_code, enum machine_mode, rtx));
+static enum rtx_code simplify_comparison  PROTO((enum rtx_code, rtx *, rtx *));
+static int reversible_comparison_p  PROTO((rtx));
+static void update_table_tick  PROTO((rtx));
+static void record_value_for_reg  PROTO((rtx, rtx, rtx));
+static void record_dead_and_set_regs_1  PROTO((rtx, rtx));
+static void record_dead_and_set_regs  PROTO((rtx));
+static int get_last_value_validate  PROTO((rtx *, int, int));
+static rtx get_last_value      PROTO((rtx));
+static int use_crosses_set_p   PROTO((rtx, int));
+static void reg_dead_at_p_1    PROTO((rtx, rtx));
+static int reg_dead_at_p       PROTO((rtx, rtx));
+static void move_deaths                PROTO((rtx, int, rtx, rtx *));
+static int reg_bitfield_target_p  PROTO((rtx, rtx));
+static void distribute_notes   PROTO((rtx, rtx, rtx, rtx, rtx, rtx));
+static void distribute_links   PROTO((rtx));
+static void mark_used_regs_combine PROTO((rtx));
 \f
 /* Main entry point for combiner.  F is the first insn of the function.
    NREGS is the first unused pseudo-reg number.  */
@@ -421,11 +461,18 @@ combine_instructions (f, nregs)
 
   combine_max_regno = nregs;
 
+  reg_nonzero_bits
+    = (unsigned HOST_WIDE_INT *) alloca (nregs * sizeof (HOST_WIDE_INT));
+  reg_sign_bit_copies = (char *) alloca (nregs * sizeof (char));
+
+  bzero (reg_nonzero_bits, nregs * sizeof (HOST_WIDE_INT));
+  bzero (reg_sign_bit_copies, nregs * sizeof (char));
+
   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_last_set_mode
     = (enum machine_mode *) alloca (nregs * sizeof (enum machine_mode));
@@ -434,21 +481,7 @@ combine_instructions (f, nregs)
   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_label, nregs * sizeof (short));
-  bzero (reg_last_set_invalid, nregs * sizeof (char));
-  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_reg_last_arrays ();
 
   init_recog_no_volatile ();
 
@@ -502,28 +535,26 @@ combine_instructions (f, nregs)
 
   /* Now scan all the insns in forward order.  */
 
+  this_basic_block = -1;
   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 (short));
-  bzero (reg_last_set_label, nregs * sizeof (short));
-  bzero (reg_last_set_invalid, nregs * sizeof (char));
-
+  init_reg_last_arrays ();
   setup_incoming_promotions ();
 
   for (insn = f; insn; insn = next ? next : NEXT_INSN (insn))
     {
       next = 0;
 
+      /* If INSN starts a new basic block, update our basic block number.  */
+      if (this_basic_block + 1 < n_basic_blocks
+         && basic_block_head[this_basic_block + 1] == insn)
+       this_basic_block++;
+
       if (GET_CODE (insn) == CODE_LABEL)
        label_tick++;
 
-      else if (GET_CODE (insn) == INSN
-              || GET_CODE (insn) == CALL_INSN
-              || GET_CODE (insn) == JUMP_INSN)
+      else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
        {
          /* Try this insn with each insn it links back to.  */
 
@@ -619,6 +650,24 @@ combine_instructions (f, nregs)
 
   nonzero_sign_valid = 0;
 }
+
+/* Wipe the reg_last_xxx arrays in preparation for another pass.  */
+
+static void
+init_reg_last_arrays ()
+{
+  int nregs = combine_max_regno;
+
+  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));
+  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));
+}
 \f
 /* Set up any promoted values for incoming argument registers.  */
 
@@ -666,10 +715,18 @@ set_nonzero_bits_and_sign_copies (x, set)
       && 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.  */
@@ -684,8 +741,31 @@ set_nonzero_bits_and_sign_copies (x, 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)) - 1))))
+           src = GEN_INT (INTVAL (src)
+                          | ((HOST_WIDE_INT) (-1)
+                             << GET_MODE_BITSIZE (GET_MODE (x))));
+#endif
+
          reg_nonzero_bits[REGNO (x)]
-           |= nonzero_bits (SET_SRC (set), nonzero_bits_mode);
+           |= 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)
@@ -791,20 +871,6 @@ can_combine_p (insn, i3, pred, succ, pdest, psrc)
 
   /* Don't eliminate a store in the stack pointer.  */
   if (dest == stack_pointer_rtx
-      /* Don't install a subreg involving two modes not tieable.
-        It can worsen register allocation, and can even make invalid reload
-        insns, since the reg inside may need to be copied from in the
-        outside mode, and that may be invalid if it is an fp reg copied in
-        integer mode.  As a special exception, we can allow this if
-        I3 is simply copying DEST, a REG,  to CC0.  */
-      || (GET_CODE (src) == SUBREG
-         && ! MODES_TIEABLE_P (GET_MODE (src), GET_MODE (SUBREG_REG (src)))
-#ifdef HAVE_cc0
-         && ! (GET_CODE (i3) == INSN && GET_CODE (PATTERN (i3)) == SET
-               && SET_DEST (PATTERN (i3)) == cc0_rtx
-               && GET_CODE (dest) == REG && dest == SET_SRC (PATTERN (i3)))
-#endif
-         )
       /* If we couldn't eliminate a field assignment, we can't combine.  */
       || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == STRICT_LOW_PART
       /* Don't combine with an insn that sets a register to itself if it has
@@ -825,10 +891,16 @@ 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).  If INSN has a REG_EQUIV note, the register is always
+        equivalent to the memory so the substitution is valid even if there
+        are intervening stores.  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) != MEM
+               || ! find_reg_note (insn, REG_EQUIV, src))
+              && use_crosses_set_p (src, INSN_CUID (insn)))
+             || (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)
@@ -856,14 +928,16 @@ can_combine_p (insn, i3, pred, succ, pdest, psrc)
       if (GET_CODE (src) == REG
          && ((REGNO (dest) < FIRST_PSEUDO_REGISTER
               && ! HARD_REGNO_MODE_OK (REGNO (dest), GET_MODE (dest)))
-#ifdef SMALL_REGISTER_CLASSES
-             /* Don't extend the life of a hard register.  */
-             || REGNO (src) < FIRST_PSEUDO_REGISTER
-#else
+             /* Don't extend the life of a hard register unless it is
+                user variable (if we have few registers) or it can't
+                fit into the desired register (meaning something special
+                is going on).  */
              || (REGNO (src) < FIRST_PSEUDO_REGISTER
-                 && ! HARD_REGNO_MODE_OK (REGNO (src), GET_MODE (src)))
+                 && (! HARD_REGNO_MODE_OK (REGNO (src), GET_MODE (src))
+#ifdef SMALL_REGISTER_CLASSES
+                     || ! REG_USERVAR_P (src)
 #endif
-         ))
+                     ))))
        return 0;
     }
   else if (GET_CODE (dest) != CC0)
@@ -890,6 +964,14 @@ can_combine_p (insn, i3, pred, succ, pdest, psrc)
          && p != succ && volatile_refs_p (PATTERN (p)))
        return 0;
 
+  /* If there are any volatile insns between INSN and I3, reject, because
+     they might affect machine state.  */
+
+  for (p = NEXT_INSN (insn); p != i3; p = NEXT_INSN (p))
+    if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
+       && p != succ && volatile_insn_p (PATTERN (p)))
+      return 0;
+
   /* If INSN or I2 contains an autoincrement or autodecrement,
      make sure that register is not used between there and I3,
      and not already used in I3 either.
@@ -959,7 +1041,8 @@ can_combine_p (insn, i3, pred, succ, pdest, psrc)
    of a SET must prevent combination from occurring.
 
    On machines where SMALL_REGISTER_CLASSES is defined, we don't combine
-   if the destination of a SET is a hard register.
+   if the destination of a SET is a hard register that isn't a user
+   variable.
 
    Before doing the above check, we first try to expand a field assignment
    into a set of logical operations.
@@ -1029,21 +1112,31 @@ combinable_i3pat (i3, loc, i2dest, i1dest, i1_not_in_src, pi3dest_killed)
             CALL operation.  */
          || (GET_CODE (inner_dest) == REG
              && REGNO (inner_dest) < FIRST_PSEUDO_REGISTER
+             && (! HARD_REGNO_MODE_OK (REGNO (inner_dest),
+                                       GET_MODE (inner_dest))
 #ifdef SMALL_REGISTER_CLASSES
-             && GET_CODE (src) != CALL
-#else
-             && ! HARD_REGNO_MODE_OK (REGNO (inner_dest),
-                                      GET_MODE (inner_dest))
+                || (GET_CODE (src) != CALL && ! REG_USERVAR_P (inner_dest))
 #endif
-             )
-
+                 ))
          || (i1_not_in_src && reg_overlap_mentioned_p (i1dest, src)))
        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 HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+         && REGNO (dest) != HARD_FRAME_POINTER_REGNUM
+#endif
+#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;
@@ -1074,8 +1167,9 @@ combinable_i3pat (i3, loc, i2dest, i1dest, i1_not_in_src, pi3dest_killed)
    and I1 is pseudo-deleted by turning it into a NOTE.  Otherwise, I1 and I2
    are pseudo-deleted.
 
-   If we created two insns, return I2; otherwise return I3.
-   Return 0 if the combination does not work.  Then nothing is changed.  */
+   Return 0 if the combination does not work.  Then nothing is changed. 
+   If we did the combination, return the insn at which combine should
+   resume scanning.  */
 
 static rtx
 try_combine (i3, i2, i1)
@@ -1100,10 +1194,12 @@ try_combine (i3, i2, i1)
   /* PATTERN (I2), or a copy of it in certain cases.  */
   rtx i2pat;
   /* Indicates if I2DEST or I1DEST is in I2SRC or I1_SRC.  */
-  int i2dest_in_i2src, i1dest_in_i1src = 0, i2dest_in_i1src = 0;
+  int i2dest_in_i2src = 0, i1dest_in_i1src = 0, i2dest_in_i1src = 0;
   int i1_feeds_i3 = 0;
   /* Notes that must be added to REG_NOTES in I3 and I2.  */
   rtx new_i3_notes, new_i2_notes;
+  /* Notes that we substituted I3 into I2 instead of the normal case.  */
+  int i3_subst_into_i2 = 0;
 
   int maxreg;
   rtx temp;
@@ -1122,6 +1218,17 @@ try_combine (i3, i2, i1)
       || find_reg_note (i3, REG_LIBCALL, NULL_RTX))
     return 0;
 
+  /* If I1 or I2 is an argument set insn, and I3 is the actual
+     CALL_INSN using it as argument, never combine the two.
+     This to prevent the elimination of insns that setup a
+     parameter register for a CALL_INSN.  */
+  if (GET_CODE (i3) == CALL_INSN
+      && (((temp = single_set (i2))
+            && find_reg_fusage (i3, USE, SET_DEST (temp)))
+         || (i1 && (temp = single_set (i1))
+              && find_reg_fusage (i3, USE, SET_DEST (temp)))))
+    return 0;
+
   combine_attempts++;
 
   undobuf.num_undo = previous_num_undos = 0;
@@ -1131,11 +1238,17 @@ try_combine (i3, i2, i1)
      accept this combination.  */
   undobuf.storage = (char *) oballoc (0);
 
+  /* Reset the hard register usage information.  */
+  CLEAR_HARD_REG_SET (newpat_used_regs);
+
   /* If I1 and I2 both feed I3, they can be in any order.  To simplify the
      code below, set I1 to be the earlier of the two insns.  */
   if (i1 && INSN_CUID (i1) > INSN_CUID (i2))
     temp = i1, i1 = i2, i2 = temp;
 
+  subst_prev_insn = 0;
+  added_links_insn = 0;
+
   /* First check for one important special-case that the code below will
      not handle.  Namely, the case where I1 is zero, I2 has multiple sets,
      and I3 is a SET whose SET_SRC is a SET_DEST in I2.  In that case,
@@ -1154,7 +1267,8 @@ try_combine (i3, i2, i1)
       && REGNO (SET_SRC (PATTERN (i3))) >= FIRST_PSEUDO_REGISTER
 #ifdef SMALL_REGISTER_CLASSES
       && (GET_CODE (SET_DEST (PATTERN (i3))) != REG
-         || REGNO (SET_DEST (PATTERN (i3))) >= FIRST_PSEUDO_REGISTER)
+         || REGNO (SET_DEST (PATTERN (i3))) >= FIRST_PSEUDO_REGISTER
+         || REG_USERVAR_P (SET_DEST (PATTERN (i3))))
 #endif
       && find_reg_note (i3, REG_DEAD, SET_SRC (PATTERN (i3)))
       && GET_CODE (PATTERN (i2)) == PARALLEL
@@ -1196,7 +1310,7 @@ try_combine (i3, i2, i1)
              subst_insn = i3;
              subst_low_cuid = INSN_CUID (i2);
 
-             added_sets_2 = 0;
+             added_sets_2 = added_sets_1 = 0;
              i2dest = SET_SRC (PATTERN (i3));
 
              /* Replace the dest in I2 with our dest and make the resulting
@@ -1206,6 +1320,7 @@ try_combine (i3, i2, i1)
                     SET_DEST (PATTERN (i3)));
 
              newpat = p2;
+             i3_subst_into_i2 = 1;
              goto validate_replacement;
            }
     }
@@ -1247,8 +1362,9 @@ try_combine (i3, i2, i1)
             never appear in the insn stream so giving it the same INSN_UID
             as I2 will not cause a problem.  */
 
-         i1 = gen_rtx (INSN, VOIDmode, INSN_UID (i2), 0, i2,
-                       XVECEXP (PATTERN (i2), 0, 1), -1, 0, 0);
+         subst_prev_insn = i1
+           = gen_rtx (INSN, VOIDmode, INSN_UID (i2), 0, i2,
+                      XVECEXP (PATTERN (i2), 0, 1), -1, 0, 0);
 
          SUBST (PATTERN (i2), XVECEXP (PATTERN (i2), 0, 0));
          SUBST (XEXP (SET_SRC (PATTERN (i2)), 0),
@@ -1547,6 +1663,9 @@ try_combine (i3, i2, i1)
      destination of I3.  */
  validate_replacement:
 
+  /* Note which hard regs this insn has as inputs.  */
+  mark_used_regs_combine (newpat);
+
   /* Is the result of combination a valid instruction?  */
   insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
 
@@ -1762,7 +1881,11 @@ try_combine (i3, i2, i1)
      we have a PARALLEL with both loads from the same memory location.
      We can split this into a load from memory followed by a register-register
      copy.  This saves at least one insn, more if register allocation can
-     eliminate the copy.  */
+     eliminate the copy.
+
+     We cannot do this if the destination of the second assignment is
+     a register that we have already assumed is zero-extended.  Similarly
+     for a SUBREG of such a register.  */
 
   else if (i1 && insn_code_number < 0 && asm_noperands (newpat) < 0
           && GET_CODE (newpat) == PARALLEL
@@ -1776,6 +1899,21 @@ try_combine (i3, i2, i1)
                                   INSN_CUID (i2))
           && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != ZERO_EXTRACT
           && GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != STRICT_LOW_PART
+          && ! (temp = SET_DEST (XVECEXP (newpat, 0, 1)),
+                (GET_CODE (temp) == REG
+                 && reg_nonzero_bits[REGNO (temp)] != 0
+                 && GET_MODE_BITSIZE (GET_MODE (temp)) < BITS_PER_WORD
+                 && GET_MODE_BITSIZE (GET_MODE (temp)) < HOST_BITS_PER_INT
+                 && (reg_nonzero_bits[REGNO (temp)]
+                     != GET_MODE_MASK (word_mode))))
+          && ! (GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) == SUBREG
+                && (temp = SUBREG_REG (SET_DEST (XVECEXP (newpat, 0, 1))),
+                    (GET_CODE (temp) == REG
+                     && reg_nonzero_bits[REGNO (temp)] != 0
+                     && GET_MODE_BITSIZE (GET_MODE (temp)) < BITS_PER_WORD
+                     && GET_MODE_BITSIZE (GET_MODE (temp)) < HOST_BITS_PER_INT
+                     && (reg_nonzero_bits[REGNO (temp)]
+                         != GET_MODE_MASK (word_mode)))))
           && ! reg_overlap_mentioned_p (SET_DEST (XVECEXP (newpat, 0, 1)),
                                         SET_SRC (XVECEXP (newpat, 0, 1)))
           && ! find_reg_note (i3, REG_UNUSED,
@@ -1819,8 +1957,8 @@ try_combine (i3, i2, i1)
             which we know will be a NOTE.  */
 
          for (insn = NEXT_INSN (i3);
-              insn && GET_CODE (insn) != CODE_LABEL
-              && GET_CODE (PREV_INSN (insn)) != JUMP_INSN;
+              insn && (this_basic_block == n_basic_blocks - 1
+                       || insn != basic_block_head[this_basic_block + 1]);
               insn = NEXT_INSN (insn))
            {
              if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
@@ -1882,11 +2020,12 @@ try_combine (i3, i2, i1)
   /* If we had to change another insn, make sure it is valid also.  */
   if (undobuf.other_insn)
     {
-      rtx other_notes = REG_NOTES (undobuf.other_insn);
       rtx other_pat = PATTERN (undobuf.other_insn);
       rtx new_other_notes;
       rtx note, next;
 
+      CLEAR_HARD_REG_SET (newpat_used_regs);
+
       other_code_number = recog_for_combine (&other_pat, undobuf.other_insn,
                                             &new_other_notes);
 
@@ -1930,8 +2069,6 @@ try_combine (i3, i2, i1)
     rtx i3notes, i2notes, i1notes = 0;
     rtx i3links, i2links, i1links = 0;
     rtx midnotes = 0;
-    int all_adjacent = (next_real_insn (i2) == i3
-                       && (i1 == 0 || next_real_insn (i1) == i2));
     register int regno;
     /* Compute which registers we expect to eliminate.  */
     rtx elim_i2 = (newi2pat || i2dest_in_i2src || i2dest_in_i1src
@@ -1973,29 +2110,39 @@ try_combine (i3, i2, i1)
     /* We had one special case above where I2 had more than one set and
        we replaced a destination of one of those sets with the destination
        of I3.  In that case, we have to update LOG_LINKS of insns later
-       in this basic block.  Note that this (expensive) case is rare.  */
-
-    if (GET_CODE (PATTERN (i2)) == PARALLEL)
-      for (i = 0; i < XVECLEN (PATTERN (i2), 0); i++)
-       if (GET_CODE (SET_DEST (XVECEXP (PATTERN (i2), 0, i))) == REG
-           && SET_DEST (XVECEXP (PATTERN (i2), 0, i)) != i2dest
-           && ! find_reg_note (i2, REG_UNUSED,
-                               SET_DEST (XVECEXP (PATTERN (i2), 0, i))))
-         {
-           register rtx insn;
+       in this basic block.  Note that this (expensive) case is rare.
 
-           for (insn = NEXT_INSN (i2); insn; insn = NEXT_INSN (insn))
-             {
-               if (insn != i3 && GET_RTX_CLASS (GET_CODE (insn)) == 'i')
-                 for (link = LOG_LINKS (insn); link; link = XEXP (link, 1))
-                   if (XEXP (link, 0) == i2)
-                     XEXP (link, 0) = i3;
+       Also, in this case, we must pretend that all REG_NOTEs for I2
+       actually came from I3, so that REG_UNUSED notes from I2 will be
+       properly handled.  */
 
-               if (GET_CODE (insn) == CODE_LABEL
-                   || GET_CODE (insn) == JUMP_INSN)
-                 break;
-             }
+    if (i3_subst_into_i2)
+      {
+       for (i = 0; i < XVECLEN (PATTERN (i2), 0); i++)
+         if (GET_CODE (SET_DEST (XVECEXP (PATTERN (i2), 0, i))) == REG
+             && SET_DEST (XVECEXP (PATTERN (i2), 0, i)) != i2dest
+             && ! find_reg_note (i2, REG_UNUSED,
+                                 SET_DEST (XVECEXP (PATTERN (i2), 0, i))))
+           for (temp = NEXT_INSN (i2);
+                temp && (this_basic_block == n_basic_blocks - 1
+                         || basic_block_head[this_basic_block] != temp);
+                temp = NEXT_INSN (temp))
+             if (temp != i3 && GET_RTX_CLASS (GET_CODE (temp)) == 'i')
+               for (link = LOG_LINKS (temp); link; link = XEXP (link, 1))
+                 if (XEXP (link, 0) == i2)
+                   XEXP (link, 0) = i3;
+
+       if (i3notes)
+         {
+           rtx link = i3notes;
+           while (XEXP (link, 1))
+             link = XEXP (link, 1);
+           XEXP (link, 1) = i2notes;
          }
+       else
+         i3notes = i2notes;
+       i2notes = 0;
+      }
 
     LOG_LINKS (i3) = 0;
     REG_NOTES (i3) = 0;
@@ -2194,7 +2341,12 @@ try_combine (i3, i2, i1)
 
   combine_successes++;
 
-  return newi2pat ? i2 : i3;
+  if (added_links_insn
+      && (newi2pat == 0 || INSN_CUID (added_links_insn) < INSN_CUID (i2))
+      && INSN_CUID (added_links_insn) < INSN_CUID (i3))
+    return added_links_insn;
+  else
+    return newi2pat ? i2 : i3;
 }
 \f
 /* Undo all the modifications recorded in undobuf.  */
@@ -2210,7 +2362,7 @@ undo_all ()
       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;
+       *undobuf.undo[i].where.r = undobuf.undo[i].old_contents.r;
       
     }
 
@@ -2602,28 +2754,11 @@ subst (x, from, to, in_dest, unique_copy)
      int in_dest;
      int unique_copy;
 {
+  register enum rtx_code code = GET_CODE (x);
+  enum machine_mode op0_mode = VOIDmode;
   register char *fmt;
   register int len, i;
-  register enum rtx_code code = GET_CODE (x), orig_code = code;
-  rtx temp;
-  enum machine_mode mode = GET_MODE (x);
-  enum machine_mode op0_mode = VOIDmode;
-  rtx other_insn;
-  rtx *cc_use;
-  int n_restarts = 0;
-
-/* FAKE_EXTEND_SAFE_P (MODE, FROM) is 1 if (subreg:MODE FROM 0) is a safe
-   replacement for (zero_extend:MODE FROM) or (sign_extend:MODE FROM).
-   If it is 0, that cannot be done.  We can now do this for any MEM
-   because (SUBREG (MEM...)) is guaranteed to cause the MEM to be reloaded.
-   If not for that, MEM's would very rarely be safe.  */
-
-/* Reject MODEs bigger than a word, because we might not be able
-   to reference a two-register group starting with an arbitrary register
-   (and currently gen_lowpart might crash for a SUBREG).  */
-
-#define FAKE_EXTEND_SAFE_P(MODE, FROM) \
-  (GET_MODE_SIZE (MODE) <= UNITS_PER_WORD)
+  rtx new;
 
 /* Two expressions are equal if they are identical copies of a shared
    RTX or if they are both registers with the same register number
@@ -2689,7 +2824,6 @@ subst (x, from, to, in_dest, unique_copy)
          register int j;
          for (j = XVECLEN (x, i) - 1; j >= 0; j--)
            {
-             register rtx new;
              if (COMBINE_RTX_EQUAL_P (XVECEXP (x, i, j), from))
                {
                  new = (unique_copy && n_occurrences ? copy_rtx (to) : to);
@@ -2709,10 +2843,30 @@ subst (x, from, to, in_dest, unique_copy)
        }
       else if (fmt[i] == 'e')
        {
-         register rtx new;
-
          if (COMBINE_RTX_EQUAL_P (XEXP (x, i), from))
            {
+             /* In general, don't install a subreg involving two modes not
+                tieable.  It can worsen register allocation, and can even
+                make invalid reload insns, since the reg inside may need to
+                be copied from in the outside mode, and that may be invalid
+                if it is an fp reg copied in integer mode.
+
+                We allow two exceptions to this: It is valid if it is inside
+                another SUBREG and the mode of that SUBREG and the mode of
+                the inside of TO is tieable and it is valid if X is a SET
+                that copies FROM to CC0.  */
+             if (GET_CODE (to) == SUBREG
+                 && ! MODES_TIEABLE_P (GET_MODE (to),
+                                       GET_MODE (SUBREG_REG (to)))
+                 && ! (code == SUBREG
+                       && MODES_TIEABLE_P (GET_MODE (x),
+                                           GET_MODE (SUBREG_REG (to))))
+#ifdef HAVE_cc0
+                 && ! (code == SET && i == 1 && XEXP (x, 0) == cc0_rtx)
+#endif
+                 )
+               return gen_rtx (CLOBBER, VOIDmode, const0_rtx);
+
              new = (unique_copy && n_occurrences ? copy_rtx (to) : to);
              n_occurrences++;
            }
@@ -2745,25 +2899,49 @@ 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.  */
+  /* Try to simplify X.  If the simplification changed the code, it is likely
+     that further simplification will help, so loop, but limit the number
+     of repetitions that will be performed.  */
 
- restart:
+  for (i = 0; i < 4; i++)
+    {
+      /* If X is sufficiently simple, don't bother trying to do anything
+        with it.  */
+      if (code != CONST_INT && code != REG && code != CLOBBER)
+       x = simplify_rtx (x, op0_mode, i == 3, in_dest);
 
-  /* If we have restarted more than 4 times, we are probably looping, so
-     give up.  */
-  if (++n_restarts > 4)
-    return x;
+      if (GET_CODE (x) == code)
+       break;
 
-  /* 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).  */
+      code = GET_CODE (x);
 
-  if (n_restarts > 1)
-    op0_mode = VOIDmode;
+      /* We no longer know the original mode of operand 0 since we
+        have changed the form of X)  */
+      op0_mode = VOIDmode;
+    }
 
-  code = GET_CODE (x);
+  return x;
+}
+\f
+/* Simplify X, a piece of RTL.  We just operate on the expression at the
+   outer level; call `subst' to simplify recursively.  Return the new
+   expression.
+
+   OP0_MODE is the original mode of XEXP (x, 0); LAST is nonzero if this
+   will be the iteration even if an expression with a code different from
+   X is returned; IN_DEST is nonzero if we are inside a SET_DEST.  */
+
+static rtx
+simplify_rtx (x, op0_mode, last, in_dest)
+     rtx x;
+     enum machine_mode op0_mode;
+     int last;
+     int in_dest;
+{
+  enum rtx_code code = GET_CODE (x);
+  enum machine_mode mode = GET_MODE (x);
+  rtx temp;
+  int i;
 
   /* 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.  */
@@ -2823,38 +3001,71 @@ subst (x, from, to, in_dest, unique_copy)
 
   /* 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)
+     things.  Check for cases where both arms are testing the same
+     condition.
+
+     Don't do anything if all operands are very simple.  */
+
+  if (((GET_RTX_CLASS (code) == '2' || GET_RTX_CLASS (code) == 'c'
+       || GET_RTX_CLASS (code) == '<')
+       && ((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')))
+          || (GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) != 'o'
+              && ! (GET_CODE (XEXP (x, 1)) == SUBREG
+                    && (GET_RTX_CLASS (GET_CODE (SUBREG_REG (XEXP (x, 1))))
+                        == 'o')))))
+      || (GET_RTX_CLASS (code) == '1'
+         && ((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'))))))
     {
-      /* 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;
-    }
+      rtx cond, true, false;
 
-  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;
+      cond = if_then_else_cond (x, &true, &false);
+      if (cond != 0)
+       {
+         rtx cop1 = const0_rtx;
+         enum rtx_code cond_code = simplify_comparison (NE, &cond, &cop1);
+
+         /* Simplify the alternative arms; this may collapse the true and 
+            false arms to store-flag values.  */
+         true = subst (true, pc_rtx, pc_rtx, 0, 0);
+         false = subst (false, pc_rtx, pc_rtx, 0, 0);
+
+         /* Restarting if we generate a store-flag expression will cause
+            us to loop.  Just drop through in this case.  */
+
+         /* If the result values are STORE_FLAG_VALUE and zero, we can
+            just make the comparison operation.  */
+         if (true == const_true_rtx && false == const0_rtx)
+           x = gen_binary (cond_code, mode, cond, cop1);
+         else if (true == const0_rtx && false == const_true_rtx)
+           x = gen_binary (reverse_condition (cond_code), mode, cond, cop1);
+
+         /* Likewise, we can make the negate of a comparison operation
+            if the result values are - STORE_FLAG_VALUE and zero.  */
+         else if (GET_CODE (true) == CONST_INT
+                  && INTVAL (true) == - STORE_FLAG_VALUE
+                  && false == const0_rtx)
+           x = gen_unary (NEG, mode,
+                          gen_binary (cond_code, mode, cond, cop1));
+         else if (GET_CODE (false) == CONST_INT
+                  && INTVAL (false) == - STORE_FLAG_VALUE
+                  && true == const0_rtx)
+           x = gen_unary (NEG, mode,
+                          gen_binary (reverse_condition (cond_code), 
+                                      mode, cond, cop1));
+         else
+           return gen_rtx (IF_THEN_ELSE, mode,
+                           gen_binary (cond_code, VOIDmode, cond, cop1),
+                           true, false);
+
+         code = GET_CODE (x);
+         op0_mode = VOIDmode;
+       }
     }
 
   /* Try to fold this expression in case we have constants that weren't
@@ -2889,7 +3100,8 @@ subst (x, from, to, in_dest, unique_copy)
     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);
@@ -2902,7 +3114,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)
        {
@@ -2936,11 +3148,7 @@ subst (x, from, to, in_dest, unique_copy)
            }
 
          if (inner)
-           {
-             x = gen_binary (code, mode, other, inner);
-             goto restart;
-           
-           }
+           return gen_binary (code, mode, other, inner);
        }
     }
 
@@ -3014,6 +3222,9 @@ subst (x, from, to, in_dest, unique_copy)
       if (GET_CODE (SUBREG_REG (x)) == REG
          && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER
          && REGNO (SUBREG_REG (x)) != FRAME_POINTER_REGNUM
+#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
+         && REGNO (SUBREG_REG (x)) != HARD_FRAME_POINTER_REGNUM
+#endif
 #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
          && REGNO (SUBREG_REG (x)) != ARG_POINTER_REGNUM
 #endif
@@ -3043,35 +3254,40 @@ 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.  */
+      /* A paradoxical SUBREG of a VOIDmode constant is the same constant,
+        since we are saying that the high bits don't matter.  */
+      if (CONSTANT_P (SUBREG_REG (x)) && GET_MODE (SUBREG_REG (x)) == VOIDmode
+         && GET_MODE_SIZE (mode) > GET_MODE_SIZE (op0_mode))
+       return SUBREG_REG (x);
+
+      /* Note that we cannot do any narrowing for non-constants since
+        we might have been counting on using the fact that some bits were
+        zero.  We now do this in the SET.  */
 
-      if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
-         && subreg_lowpart_p (x))
-       return force_to_mode (SUBREG_REG (x), mode, GET_MODE_BITSIZE (mode),
-                             NULL_RTX);
       break;
 
     case NOT:
       /* (not (plus X -1)) can become (neg X).  */
       if (GET_CODE (XEXP (x, 0)) == PLUS
          && XEXP (XEXP (x, 0), 1) == constm1_rtx)
-       {
-         x = gen_rtx_combine (NEG, mode, XEXP (XEXP (x, 0), 0));
-         goto restart;
-       }
+       return gen_rtx_combine (NEG, mode, XEXP (XEXP (x, 0), 0));
 
       /* Similarly, (not (neg X)) is (plus X -1).  */
       if (GET_CODE (XEXP (x, 0)) == NEG)
-       {
-         x = gen_rtx_combine (PLUS, mode, XEXP (XEXP (x, 0), 0), constm1_rtx);
-         goto restart;
-       }
+       return gen_rtx_combine (PLUS, mode, XEXP (XEXP (x, 0), 0),
+                               constm1_rtx);
 
       /* (not (xor X C)) for C constant is (xor X D) with D = ~ C.  */
       if (GET_CODE (XEXP (x, 0)) == XOR
@@ -3090,11 +3306,8 @@ subst (x, from, to, in_dest, unique_copy)
         but this doesn't seem common enough to bother with.  */
       if (GET_CODE (XEXP (x, 0)) == ASHIFT
          && XEXP (XEXP (x, 0), 0) == const1_rtx)
-       {
-         x = gen_rtx (ROTATE, mode, gen_unary (NOT, mode, const1_rtx),
-                      XEXP (XEXP (x, 0), 1));
-         goto restart;
-       }
+       return gen_rtx (ROTATE, mode, gen_unary (NOT, mode, const1_rtx),
+                       XEXP (XEXP (x, 0), 1));
                                            
       if (GET_CODE (XEXP (x, 0)) == SUBREG
          && subreg_lowpart_p (XEXP (x, 0))
@@ -3108,8 +3321,7 @@ subst (x, from, to, in_dest, unique_copy)
          x = gen_rtx (ROTATE, inner_mode,
                       gen_unary (NOT, inner_mode, const1_rtx),
                       XEXP (SUBREG_REG (XEXP (x, 0)), 1));
-         x = gen_lowpart_for_combine (mode, x);
-         goto restart;
+         return gen_lowpart_for_combine (mode, x);
        }
                                            
 #if STORE_FLAG_VALUE == -1
@@ -3160,9 +3372,8 @@ subst (x, from, to, in_dest, unique_copy)
             in2 = in1; in1 = tem;
           }
 
-        x = gen_rtx_combine (GET_CODE (XEXP (x, 0)) == IOR ? AND : IOR,
-                             mode, in1, in2);
-        goto restart;
+        return gen_rtx_combine (GET_CODE (XEXP (x, 0)) == IOR ? AND : IOR,
+                                mode, in1, in2);
        } 
       break;
 
@@ -3170,36 +3381,25 @@ subst (x, from, to, in_dest, unique_copy)
       /* (neg (plus X 1)) can become (not X).  */
       if (GET_CODE (XEXP (x, 0)) == PLUS
          && XEXP (XEXP (x, 0), 1) == const1_rtx)
-       {
-         x = gen_rtx_combine (NOT, mode, XEXP (XEXP (x, 0), 0));
-         goto restart;
-       }
+       return gen_rtx_combine (NOT, mode, XEXP (XEXP (x, 0), 0));
 
       /* Similarly, (neg (not X)) is (plus X 1).  */
       if (GET_CODE (XEXP (x, 0)) == NOT)
-       {
-         x = gen_rtx_combine (PLUS, mode, XEXP (XEXP (x, 0), 0), const1_rtx);
-         goto restart;
-       }
+       return plus_constant (XEXP (XEXP (x, 0), 0), 1);
 
       /* (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))
-       {
-         x = gen_binary (MINUS, mode, XEXP (XEXP (x, 0), 1),
-                         XEXP (XEXP (x, 0), 0));
-         goto restart;
-       }
+             || TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+             || flag_fast_math))
+       return gen_binary (MINUS, mode, XEXP (XEXP (x, 0), 1),
+                          XEXP (XEXP (x, 0), 0));
 
       /* (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;
-       }
+       return gen_binary (PLUS, mode, XEXP (XEXP (x, 0), 0), constm1_rtx);
 
       /* NEG commutes with ASHIFT since it is multiplication.  Only do this
         if we can then eliminate the NEG (e.g.,
@@ -3225,11 +3425,8 @@ subst (x, from, to, in_dest, unique_copy)
       if (GET_CODE (temp) == ASHIFTRT
          && GET_CODE (XEXP (temp, 1)) == CONST_INT
          && INTVAL (XEXP (temp, 1)) == GET_MODE_BITSIZE (mode) - 1)
-       {
-         x = simplify_shift_const (temp, LSHIFTRT, mode, XEXP (temp, 0),
-                                   INTVAL (XEXP (temp, 1)));
-         goto restart;
-       }
+       return simplify_shift_const (temp, LSHIFTRT, mode, XEXP (temp, 0),
+                                    INTVAL (XEXP (temp, 1)));
 
       /* 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
@@ -3255,10 +3452,7 @@ subst (x, from, to, in_dest, unique_copy)
          if (GET_CODE (temp1) != ASHIFTRT
              || GET_CODE (XEXP (temp1, 0)) != ASHIFT
              || XEXP (XEXP (temp1, 0), 0) != temp)
-           {
-             x = temp1;
-             goto restart;
-           }
+           return temp1;
        }
       break;
 
@@ -3267,6 +3461,15 @@ subst (x, from, to, in_dest, unique_copy)
       if (GET_CODE (XEXP (x, 0)) == FLOAT_EXTEND
          && GET_MODE (XEXP (XEXP (x, 0), 0)) == mode)
        return XEXP (XEXP (x, 0), 0);
+
+      /* (float_truncate:SF (OP:DF (float_extend:DF foo:sf))) is
+        (OP:SF foo:SF) if OP is NEG or ABS.  */
+      if ((GET_CODE (XEXP (x, 0)) == ABS
+          || GET_CODE (XEXP (x, 0)) == NEG)
+         && GET_CODE (XEXP (XEXP (x, 0), 0)) == FLOAT_EXTEND
+         && GET_MODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == mode)
+       return gen_unary (GET_CODE (XEXP (x, 0)),
+                         mode, XEXP (XEXP (XEXP (x, 0), 0), 0));
       break;  
 
 #ifdef HAVE_cc0
@@ -3279,7 +3482,8 @@ 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)))
+          || flag_fast_math)
          && XEXP (x, 1) == CONST0_RTX (GET_MODE (XEXP (x, 0))))
        return XEXP (x, 0);
       break;
@@ -3333,17 +3537,28 @@ subst (x, from, to, in_dest, unique_copy)
              || (GET_CODE (XEXP (XEXP (x, 0), 0)) == ZERO_EXTEND
                  && (GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (XEXP (x, 0), 0), 0)))
                      == i + 1))))
-       {
-         x = simplify_shift_const
-           (NULL_RTX, ASHIFTRT, mode,
-            simplify_shift_const (NULL_RTX, ASHIFT, mode,
-                                  XEXP (XEXP (XEXP (x, 0), 0), 0),
-                                  GET_MODE_BITSIZE (mode) - (i + 1)),
-            GET_MODE_BITSIZE (mode) - (i + 1));
-         goto restart;
-       }
-
-      /* If only the low-order bit of X is possible nonzero, (plus x -1)
+       return simplify_shift_const
+         (NULL_RTX, ASHIFTRT, mode,
+          simplify_shift_const (NULL_RTX, ASHIFT, mode,
+                                XEXP (XEXP (XEXP (x, 0), 0), 0),
+                                GET_MODE_BITSIZE (mode) - (i + 1)),
+          GET_MODE_BITSIZE (mode) - (i + 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)))
+       return
+         gen_unary (NEG, mode,
+                    gen_binary (reverse_condition (GET_CODE (XEXP (x, 0))),
+                                mode, XEXP (XEXP (x, 0), 0),
+                                XEXP (XEXP (x, 0), 1)));
+
+      /* 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;"  */
@@ -3352,16 +3567,12 @@ subst (x, from, to, in_dest, unique_copy)
          && ! (GET_CODE (XEXP (x,0)) == SUBREG
                && GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG)
          && nonzero_bits (XEXP (x, 0), mode) == 1)
-       {
-         x = simplify_shift_const
-           (NULL_RTX, ASHIFTRT, mode,
-            simplify_shift_const (NULL_RTX, ASHIFT, mode,
-                                  gen_rtx_combine (XOR, mode,
-                                                   XEXP (x, 0), const1_rtx),
-                                  GET_MODE_BITSIZE (mode) - 1),
-            GET_MODE_BITSIZE (mode) - 1);
-         goto restart;
-       }
+       return simplify_shift_const (NULL_RTX, ASHIFTRT, mode,
+          simplify_shift_const (NULL_RTX, ASHIFT, mode,
+                                gen_rtx_combine (XOR, mode,
+                                                 XEXP (x, 0), const1_rtx),
+                                GET_MODE_BITSIZE (mode) - 1),
+          GET_MODE_BITSIZE (mode) - 1);
 
       /* If we are adding two things that have no bits in common, convert
         the addition into an IOR.  This will often be further simplified,
@@ -3371,24 +3582,37 @@ subst (x, from, to, in_dest, unique_copy)
       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;
-       }
+       return gen_binary (IOR, mode, XEXP (x, 0), XEXP (x, 1));
       break;
 
     case MINUS:
+#if STORE_FLAG_VALUE == 1
+      /* (minus 1 (comparison foo bar)) can be done by reversing the comparison
+        code if valid.  */
+      if (XEXP (x, 0) == const1_rtx
+         && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<'
+         && reversible_comparison_p (XEXP (x, 1)))
+       return gen_binary (reverse_condition (GET_CODE (XEXP (x, 1))),
+                          mode, XEXP (XEXP (x, 1), 0),
+                               XEXP (XEXP (x, 1), 1));
+#endif
+
       /* (minus <foo> (and <foo> (const_int -pow2))) becomes
         (and <foo> (const_int pow2-1))  */
       if (GET_CODE (XEXP (x, 1)) == AND
          && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT
          && exact_log2 (- INTVAL (XEXP (XEXP (x, 1), 1))) >= 0
          && rtx_equal_p (XEXP (XEXP (x, 1), 0), XEXP (x, 0)))
-       {
-         x = simplify_and_const_int (NULL_RTX, mode, XEXP (x, 0),
-                                     - INTVAL (XEXP (XEXP (x, 1), 1)) - 1);
-         goto restart;
-       }
+       return simplify_and_const_int (NULL_RTX, mode, XEXP (x, 0),
+                                      - INTVAL (XEXP (XEXP (x, 1), 1)) - 1);
+
+      /* Canonicalize (minus A (plus B C)) to (minus (minus A B) C) for
+        integers.  */
+      if (GET_CODE (XEXP (x, 1)) == PLUS && INTEGRAL_MODE_P (mode))
+       return gen_binary (MINUS, mode,
+                          gen_binary (MINUS, mode, XEXP (x, 0),
+                                      XEXP (XEXP (x, 1), 0)),
+                          XEXP (XEXP (x, 1), 1));
       break;
 
     case MULT:
@@ -3406,29 +3630,8 @@ subst (x, from, to, in_dest, unique_copy)
                                     XEXP (XEXP (x, 0), 1), XEXP (x, 1))));
 
          if (GET_CODE (x) != MULT)
-           goto restart;
-       }
-
-      /* If this is multiplication by a power of two and its first operand is
-        a shift, treat the multiply as a shift to allow the shifts to
-        possibly combine.  */
-      if (GET_CODE (XEXP (x, 1)) == CONST_INT
-         && (i = exact_log2 (INTVAL (XEXP (x, 1)))) >= 0
-         && (GET_CODE (XEXP (x, 0)) == ASHIFT
-             || GET_CODE (XEXP (x, 0)) == LSHIFTRT
-             || GET_CODE (XEXP (x, 0)) == ASHIFTRT
-             || GET_CODE (XEXP (x, 0)) == ROTATE
-             || GET_CODE (XEXP (x, 0)) == ROTATERT))
-       {
-         x = simplify_shift_const (NULL_RTX, ASHIFT, mode, XEXP (x, 0), i);
-         goto restart;
+           return x;
        }
-
-      /* Convert (mult (ashift (const_int 1) A) B) to (ashift B A).  */
-      if (GET_CODE (XEXP (x, 0)) == ASHIFT
-         && XEXP (XEXP (x, 0), 0) == const1_rtx)
-       return gen_rtx_combine (ASHIFT, mode, XEXP (x, 1),
-                               XEXP (XEXP (x, 0), 1));
       break;
 
     case UDIV:
@@ -3441,10 +3644,7 @@ subst (x, from, to, in_dest, unique_copy)
              || GET_CODE (XEXP (x, 0)) == ASHIFTRT
              || GET_CODE (XEXP (x, 0)) == ROTATE
              || GET_CODE (XEXP (x, 0)) == ROTATERT))
-       {
-         x = simplify_shift_const (NULL_RTX, LSHIFTRT, mode, XEXP (x, 0), i);
-         goto restart;
-       }
+       return simplify_shift_const (NULL_RTX, LSHIFTRT, mode, XEXP (x, 0), i);
       break;
 
     case EQ:  case NE:
@@ -3472,43 +3672,88 @@ 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 possibly nonzero in X (such as when
-            X is a ZERO_EXTRACT of one bit.  Similarly, we can convert
-            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.  */
+            X is a ZERO_EXTRACT of one bit).  Similarly, we can convert EQ to
+            (xor X 1) or (minus 1 X); we use the former.  Finally, if X is
+            known to be either 0 or -1, NE becomes a NEG and EQ becomes
+            (plus 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
-             && nonzero_bits (op0, GET_MODE (op0)) == 1)
+             && nonzero_bits (op0, mode) == 1)
            return gen_lowpart_for_combine (mode,
                                            expand_compound_operation (op0));
+
+         else if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
+                  && op1 == const0_rtx
+                  && (num_sign_bit_copies (op0, mode)
+                      == GET_MODE_BITSIZE (mode)))
+           {
+             op0 = expand_compound_operation (op0);
+             return gen_unary (NEG, mode,
+                               gen_lowpart_for_combine (mode, op0));
+           }
+
          else if (new_code == EQ && GET_MODE_CLASS (mode) == MODE_INT
                   && op1 == const0_rtx
-                  && nonzero_bits (op0, GET_MODE (op0)) == 1)
+                  && nonzero_bits (op0, mode) == 1)
            {
              op0 = expand_compound_operation (op0);
+             return gen_binary (XOR, mode,
+                                gen_lowpart_for_combine (mode, op0),
+                                const1_rtx);
+           }
 
-             x = gen_rtx_combine (XOR, mode,
-                                  gen_lowpart_for_combine (mode, op0),
-                                  const1_rtx);
-             goto restart;
+         else if (new_code == EQ && GET_MODE_CLASS (mode) == MODE_INT
+                  && op1 == const0_rtx
+                  && (num_sign_bit_copies (op0, mode)
+                      == GET_MODE_BITSIZE (mode)))
+           {
+             op0 = expand_compound_operation (op0);
+             return plus_constant (gen_lowpart_for_combine (mode, op0), 1);
            }
 #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 can be nonzero.
-            This converts (ne (zero_extract X 1 Y) 0) to
-            (sign_extract X 1 Y).  */
+         /* If STORE_FLAG_VALUE is -1, we have cases similar to
+            those above.  */
          if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
              && op1 == const0_rtx
-             && nonzero_bits (op0, GET_MODE (op0)) == 1)
+             && (num_sign_bit_copies (op0, mode)
+                 == GET_MODE_BITSIZE (mode)))
+           return gen_lowpart_for_combine (mode,
+                                           expand_compound_operation (op0));
+
+         else if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
+                  && op1 == const0_rtx
+                  && nonzero_bits (op0, mode) == 1)
+           {
+             op0 = expand_compound_operation (op0);
+             return gen_unary (NEG, mode,
+                               gen_lowpart_for_combine (mode, op0));
+           }
+
+         else if (new_code == EQ && GET_MODE_CLASS (mode) == MODE_INT
+                  && op1 == const0_rtx
+                  && (num_sign_bit_copies (op0, mode)
+                      == GET_MODE_BITSIZE (mode)))
+           {
+             op0 = expand_compound_operation (op0);
+             return gen_unary (NOT, mode,
+                               gen_lowpart_for_combine (mode, op0));
+           }
+
+         /* If X is 0/1, (eq X 0) is X-1.  */
+         else if (new_code == EQ && GET_MODE_CLASS (mode) == MODE_INT
+                  && op1 == const0_rtx
+                  && nonzero_bits (op0, mode) == 1)
            {
              op0 = expand_compound_operation (op0);
-             x = gen_rtx_combine (NEG, mode,
-                                  gen_lowpart_for_combine (mode, op0));
-             goto restart;
+             return plus_constant (gen_lowpart_for_combine (mode, op0), -1);
            }
 #endif
 
@@ -3523,7 +3768,7 @@ subst (x, from, to, in_dest, unique_copy)
                  == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))
              && op1 == const0_rtx
              && mode == GET_MODE (op0)
-             && (i = exact_log2 (nonzero_bits (op0, GET_MODE (op0)))) >= 0)
+             && (i = exact_log2 (nonzero_bits (op0, mode))) >= 0)
            {
              x = simplify_shift_const (NULL_RTX, ASHIFT, mode,
                                        expand_compound_operation (op0),
@@ -3546,581 +3791,720 @@ 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.  */
+      return simplify_if_then_else (x);
 
-      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);
-           }
+    case ZERO_EXTRACT:
+    case SIGN_EXTRACT:
+    case ZERO_EXTEND:
+    case SIGN_EXTEND:
+      /* If we are processing SET_DEST, we are done. */
+      if (in_dest)
+       return x;
 
-         /* 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.  Similarly if we have a constant in that
-        position or if the third operand is the same as the first operand
-        of the comparison.  */
+      return expand_compound_operation (x);
 
-      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_binary (reverse_condition (GET_CODE (XEXP (x, 0))),
-                            GET_MODE (XEXP (x, 0)),
-                            XEXP (XEXP (x, 0), 0), XEXP (XEXP (x, 0), 1)));
+    case SET:
+      return simplify_set (x);
 
-         temp = XEXP (x, 1);
-         SUBST (XEXP (x, 1), XEXP (x, 2));
-         SUBST (XEXP (x, 2), temp);
-       }
+    case AND:
+    case IOR:
+    case XOR:
+      return simplify_logical (x, last);
 
-      /* If the two arms are identical, we don't need the comparison.  */
+    case ABS:
+      /* (abs (neg <foo>)) -> (abs <foo>) */
+      if (GET_CODE (XEXP (x, 0)) == NEG)
+       SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0));
 
-      if (rtx_equal_p (XEXP (x, 1), XEXP (x, 2))
-         && ! side_effects_p (XEXP (x, 0)))
-       return XEXP (x, 1);
+      /* If operand is something known to be positive, ignore the ABS.  */
+      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)
+             && ((nonzero_bits (XEXP (x, 0), GET_MODE (XEXP (x, 0)))
+                  & ((HOST_WIDE_INT) 1
+                     << (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - 1)))
+                 == 0)))
+       return XEXP (x, 0);
 
-      /* 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;
-         }
+      /* If operand is known to be only -1 or 0, convert ABS to NEG.  */
+      if (num_sign_bit_copies (XEXP (x, 0), mode) == GET_MODE_BITSIZE (mode))
+       return gen_rtx_combine (NEG, mode, XEXP (x, 0));
 
-      /* Look for MIN or MAX.  */
+      break;
 
-      if (GET_MODE_CLASS (mode) == MODE_INT
-         && 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;
-         }
+    case FFS:
+      /* (ffs (*_extend <X>)) = (ffs <X>) */
+      if (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND
+         || GET_CODE (XEXP (x, 0)) == ZERO_EXTEND)
+       SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0));
+      break;
+
+    case FLOAT:
+      /* (float (sign_extend <X>)) = (float <X>).  */
+      if (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND)
+       SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0));
+      break;
 
-      /* 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))))
+    case ASHIFT:
+    case LSHIFTRT:
+    case ASHIFTRT:
+    case ROTATE:
+    case ROTATERT:
+      /* If this is a shift by a constant amount, simplify it.  */
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+       return simplify_shift_const (x, code, mode, XEXP (x, 0), 
+                                    INTVAL (XEXP (x, 1)));
+
+#ifdef SHIFT_COUNT_TRUNCATED
+      else if (SHIFT_COUNT_TRUNCATED && GET_CODE (XEXP (x, 1)) != REG)
+       SUBST (XEXP (x, 1),
+              force_to_mode (XEXP (x, 1), GET_MODE (x),
+                             ((HOST_WIDE_INT) 1 
+                              << exact_log2 (GET_MODE_BITSIZE (GET_MODE (x))))
+                             - 1,
+                             NULL_RTX, 0));
+#endif
+
+      break;
+    }
+
+  return x;
+}
+\f
+/* Simplify X, an IF_THEN_ELSE expression.  Return the new expression.  */
+
+static rtx
+simplify_if_then_else (x)
+     rtx x;
+{
+  enum machine_mode mode = GET_MODE (x);
+  rtx cond = XEXP (x, 0);
+  rtx true = XEXP (x, 1);
+  rtx false = XEXP (x, 2);
+  enum rtx_code true_code = GET_CODE (cond);
+  int comparison_p = GET_RTX_CLASS (true_code) == '<';
+  rtx temp;
+  int i;
+
+  /* Simplify storing of the truth value. */
+  if (comparison_p && true == const_true_rtx && false == const0_rtx)
+    return gen_binary (true_code, mode, XEXP (cond, 0), XEXP (cond, 1));
+      
+  /* Also when the truth value has to be reversed. */
+  if (comparison_p && reversible_comparison_p (cond)
+      && true == const0_rtx && false == const_true_rtx)
+    return gen_binary (reverse_condition (true_code),
+                      mode, XEXP (cond, 0), XEXP (cond, 1));
+
+  /* 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 (comparison_p && reversible_comparison_p (cond)
+      && GET_CODE (XEXP (cond, 0)) == REG)
+    {
+      HOST_WIDE_INT nzb;
+      rtx from = XEXP (cond, 0);
+      enum rtx_code false_code = reverse_condition (true_code);
+      rtx true_val = XEXP (cond, 1);
+      rtx false_val = true_val;
+      int swapped = 0;
+
+      /* If FALSE_CODE is EQ, swap the codes and arms.  */
+
+      if (false_code == EQ)
        {
-         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));
-           }
+         swapped = 1, true_code = EQ, false_code = NE;
+         temp = true, true = false, false = temp;
+       }
 
-         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));
+      /* 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 careful due to the potential
+        of locally-shared RTL.  */
+
+      if (reg_mentioned_p (from, true))
+       true = subst (known_cond (copy_rtx (true), true_code, from, true_val),
+                     pc_rtx, pc_rtx, 0, 0);
+      if (reg_mentioned_p (from, false))
+       false = subst (known_cond (copy_rtx (false), false_code,
+                                  from, false_val),
+                      pc_rtx, pc_rtx, 0, 0);
+
+      SUBST (XEXP (x, 1), swapped ? false : true);
+      SUBST (XEXP (x, 2), swapped ? true : false);
+
+      true = XEXP (x, 1), false = XEXP (x, 2), true_code = GET_CODE (cond);
+    }
 
-             temp = gen_binary (op, m, gen_lowpart_for_combine (m, z), temp);
+  /* 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.  Similarly if we have a constant in the true
+     arm, the false arm is the same as the first operand of the comparison, or
+     the false arm is more complicated than the true arm.  */
+
+  if (comparison_p && reversible_comparison_p (cond)
+      && (true == pc_rtx 
+         || (CONSTANT_P (true)
+             && GET_CODE (false) != CONST_INT && false != pc_rtx)
+         || true == const0_rtx
+         || (GET_RTX_CLASS (GET_CODE (true)) == 'o'
+             && GET_RTX_CLASS (GET_CODE (false)) != 'o')
+         || (GET_CODE (true) == SUBREG
+             && GET_RTX_CLASS (GET_CODE (SUBREG_REG (true))) == 'o'
+             && GET_RTX_CLASS (GET_CODE (false)) != 'o')
+         || reg_mentioned_p (true, false)
+         || rtx_equal_p (false, XEXP (cond, 0))))
+    {
+      true_code = reverse_condition (true_code);
+      SUBST (XEXP (x, 0),
+            gen_binary (true_code, GET_MODE (cond), XEXP (cond, 0),
+                        XEXP (cond, 1)));
 
-             if (extend_op != 0)
-               temp = gen_unary (extend_op, mode, temp);
+      SUBST (XEXP (x, 1), false);
+      SUBST (XEXP (x, 2), true);
 
-             return temp;
-           }
+      temp = true, true = false, false = temp, cond = XEXP (x, 0);
+    }
+
+  /* If the two arms are identical, we don't need the comparison.  */
+
+  if (rtx_equal_p (true, false) && ! side_effects_p (cond))
+    return true;
+
+  /* Look for cases where we have (abs x) or (neg (abs X)).  */
+
+  if (GET_MODE_CLASS (mode) == MODE_INT
+      && GET_CODE (false) == NEG
+      && rtx_equal_p (true, XEXP (false, 0))
+      && comparison_p
+      && rtx_equal_p (true, XEXP (cond, 0))
+      && ! side_effects_p (true))
+    switch (true_code)
+      {
+      case GT:
+      case GE:
+       return gen_unary (ABS, mode, true);
+      case LT:
+      case LE:
+       return gen_unary (NEG, mode, gen_unary (ABS, mode, true));
+      }
+
+  /* Look for MIN or MAX.  */
+
+  if ((! FLOAT_MODE_P (mode) | flag_fast_math)
+      && comparison_p
+      && rtx_equal_p (XEXP (cond, 0), true)
+      && rtx_equal_p (XEXP (cond, 1), false)
+      && ! side_effects_p (cond))
+    switch (true_code)
+      {
+      case GE:
+      case GT:
+       return gen_binary (SMAX, mode, true, false);
+      case LE:
+      case LT:
+       return gen_binary (SMIN, mode, true, false);
+      case GEU:
+      case GTU:
+       return gen_binary (UMAX, mode, true, false);
+      case LEU:
+      case LTU:
+       return gen_binary (UMIN, mode, true, false);
+      }
+  
+#if STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1
+
+  /* If we have (if_then_else COND (OP Z C1) Z) and OP is an identity when its
+     second operand is zero, this can be done as (OP Z (mult COND C2)) where
+     C2 = C1 * STORE_FLAG_VALUE. Similarly if OP has an outer ZERO_EXTEND or
+     SIGN_EXTEND as long as Z is already extended (so we don't destroy it).
+     We can do this kind of thing in some cases when STORE_FLAG_VALUE is
+     neither of the above, but it isn't worth checking for.
+
+     Similarly, (if_then_else COND Z 0) can be replaced by 
+     (mult COND (mult Z STORE_FLAG_VALUE)).  */
+
+  if (comparison_p && mode != VOIDmode && ! side_effects_p (x))
+    {
+      rtx t = make_compound_operation (true, SET);
+      rtx f = make_compound_operation (false, SET);
+      rtx cond_op0 = XEXP (cond, 0);
+      rtx cond_op1 = XEXP (cond, 1);
+      enum rtx_code op, extend_op = NIL;
+      enum machine_mode m = mode;
+      rtx z = 0, c1;
+
+      if (f == const0_rtx)
+       return gen_binary (MULT, mode, gen_binary (true_code, mode, cond_op0,
+                                                  cond_op1),
+                          gen_binary (MULT, mode, t, const_true_rtx));
+
+      if ((GET_CODE (t) == PLUS || GET_CODE (t) == MINUS
+          || GET_CODE (t) == IOR || GET_CODE (t) == XOR
+          || GET_CODE (t) == ASHIFT
+          || GET_CODE (t) == LSHIFTRT || GET_CODE (t) == ASHIFTRT)
+         && rtx_equal_p (XEXP (t, 0), f))
+       c1 = XEXP (t, 1), op = GET_CODE (t), z = f;
+
+      /* If an identity-zero op is commutative, check whether there
+        would be a match if we swapped the operands. */
+      else if ((GET_CODE (t) == PLUS || GET_CODE (t) == IOR
+               || GET_CODE (t) == XOR)
+              && rtx_equal_p (XEXP (t, 1), f))
+       c1 = XEXP (t, 0), op = GET_CODE (t), z = f;
+      else if (GET_CODE (t) == SIGN_EXTEND
+              && (GET_CODE (XEXP (t, 0)) == PLUS
+                  || GET_CODE (XEXP (t, 0)) == MINUS
+                  || GET_CODE (XEXP (t, 0)) == IOR
+                  || GET_CODE (XEXP (t, 0)) == XOR
+                  || GET_CODE (XEXP (t, 0)) == ASHIFT
+                  || GET_CODE (XEXP (t, 0)) == LSHIFTRT
+                  || GET_CODE (XEXP (t, 0)) == ASHIFTRT)
+              && GET_CODE (XEXP (XEXP (t, 0), 0)) == SUBREG
+              && subreg_lowpart_p (XEXP (XEXP (t, 0), 0))
+              && rtx_equal_p (SUBREG_REG (XEXP (XEXP (t, 0), 0)), f)
+              && (num_sign_bit_copies (f, GET_MODE (f))
+                  > (GET_MODE_BITSIZE (mode)
+                     - GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (t, 0), 0))))))
+       {
+         c1 = XEXP (XEXP (t, 0), 1); z = f; op = GET_CODE (XEXP (t, 0));
+         extend_op = SIGN_EXTEND;
+         m = GET_MODE (XEXP (t, 0));
        }
-      break;
-         
-    case ZERO_EXTRACT:
-    case SIGN_EXTRACT:
-    case ZERO_EXTEND:
-    case SIGN_EXTEND:
-      /* If we are processing SET_DEST, we are done. */
-      if (in_dest)
-       return x;
+      else if (GET_CODE (t) == SIGN_EXTEND
+              && (GET_CODE (XEXP (t, 0)) == PLUS
+                  || GET_CODE (XEXP (t, 0)) == IOR
+                  || GET_CODE (XEXP (t, 0)) == XOR)
+              && GET_CODE (XEXP (XEXP (t, 0), 1)) == SUBREG
+              && subreg_lowpart_p (XEXP (XEXP (t, 0), 1))
+              && rtx_equal_p (SUBREG_REG (XEXP (XEXP (t, 0), 1)), f)
+              && (num_sign_bit_copies (f, GET_MODE (f))
+                  > (GET_MODE_BITSIZE (mode)
+                     - GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (t, 0), 1))))))
+       {
+         c1 = XEXP (XEXP (t, 0), 0); z = f; op = GET_CODE (XEXP (t, 0));
+         extend_op = SIGN_EXTEND;
+         m = GET_MODE (XEXP (t, 0));
+       }
+      else if (GET_CODE (t) == ZERO_EXTEND
+              && (GET_CODE (XEXP (t, 0)) == PLUS
+                  || GET_CODE (XEXP (t, 0)) == MINUS
+                  || GET_CODE (XEXP (t, 0)) == IOR
+                  || GET_CODE (XEXP (t, 0)) == XOR
+                  || GET_CODE (XEXP (t, 0)) == ASHIFT
+                  || GET_CODE (XEXP (t, 0)) == LSHIFTRT
+                  || GET_CODE (XEXP (t, 0)) == ASHIFTRT)
+              && GET_CODE (XEXP (XEXP (t, 0), 0)) == SUBREG
+              && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+              && subreg_lowpart_p (XEXP (XEXP (t, 0), 0))
+              && rtx_equal_p (SUBREG_REG (XEXP (XEXP (t, 0), 0)), f)
+              && ((nonzero_bits (f, GET_MODE (f))
+                   & ~ GET_MODE_MASK (GET_MODE (XEXP (XEXP (t, 0), 0))))
+                  == 0))
+       {
+         c1 = XEXP (XEXP (t, 0), 1); z = f; op = GET_CODE (XEXP (t, 0));
+         extend_op = ZERO_EXTEND;
+         m = GET_MODE (XEXP (t, 0));
+       }
+      else if (GET_CODE (t) == ZERO_EXTEND
+              && (GET_CODE (XEXP (t, 0)) == PLUS
+                  || GET_CODE (XEXP (t, 0)) == IOR
+                  || GET_CODE (XEXP (t, 0)) == XOR)
+              && GET_CODE (XEXP (XEXP (t, 0), 1)) == SUBREG
+              && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+              && subreg_lowpart_p (XEXP (XEXP (t, 0), 1))
+              && rtx_equal_p (SUBREG_REG (XEXP (XEXP (t, 0), 1)), f)
+              && ((nonzero_bits (f, GET_MODE (f))
+                   & ~ GET_MODE_MASK (GET_MODE (XEXP (XEXP (t, 0), 1))))
+                  == 0))
+       {
+         c1 = XEXP (XEXP (t, 0), 0); z = f; op = GET_CODE (XEXP (t, 0));
+         extend_op = ZERO_EXTEND;
+         m = GET_MODE (XEXP (t, 0));
+       }
+      
+      if (z)
+       {
+         temp = subst (gen_binary (true_code, m, cond_op0, cond_op1),
+                       pc_rtx, pc_rtx, 0, 0);
+         temp = gen_binary (MULT, m, temp,
+                            gen_binary (MULT, m, c1, const_true_rtx));
+         temp = subst (temp, pc_rtx, pc_rtx, 0, 0);
+         temp = gen_binary (op, m, gen_lowpart_for_combine (m, z), temp);
 
-      x = expand_compound_operation (x);
-      if (GET_CODE (x) != code)
-       goto restart;
-      break;
+         if (extend_op != NIL)
+           temp = gen_unary (extend_op, mode, temp);
 
-    case SET:
-      /* (set (pc) (return)) gets written as (return).  */
-      if (GET_CODE (SET_DEST (x)) == PC && GET_CODE (SET_SRC (x)) == RETURN)
-       return SET_SRC (x);
+         return temp;
+       }
+    }
+#endif
+
+  /* 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 more generally, but it doesn't seem worth it.  */
+
+  if (true_code == NE && XEXP (cond, 1) == const0_rtx
+      && false == const0_rtx && GET_CODE (true) == CONST_INT
+      && ((1 == nonzero_bits (XEXP (cond, 0), mode)
+          && (i = exact_log2 (INTVAL (true))) >= 0)
+         || ((num_sign_bit_copies (XEXP (cond, 0), mode)
+              == GET_MODE_BITSIZE (mode))
+             && (i = exact_log2 (- INTVAL (true))) >= 0)))
+    return
+      simplify_shift_const (NULL_RTX, ASHIFT, mode,
+                           gen_lowpart_for_combine (mode, XEXP (cond, 0)), i);
+
+  return x;
+}
+\f
+/* Simplify X, a SET expression.  Return the new expression.  */
+
+static rtx
+simplify_set (x)
+     rtx x;
+{
+  rtx src = SET_SRC (x);
+  rtx dest = SET_DEST (x);
+  enum machine_mode mode
+    = GET_MODE (src) != VOIDmode ? GET_MODE (src) : GET_MODE (dest);
+  rtx other_insn;
+  rtx *cc_use;
+
+  /* (set (pc) (return)) gets written as (return).  */
+  if (GET_CODE (dest) == PC && GET_CODE (src) == RETURN)
+    return src;
 
-      /* Convert this into a field assignment operation, if possible.  */
-      x = make_field_assignment (x);
+  /* Now that we know for sure which bits of SRC we are using, see if we can
+     simplify the expression for the object knowing that we only need the
+     low-order bits.  */
 
-      /* 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.  */
-      if ((GET_CODE (SET_SRC (x)) == COMPARE
+  if (GET_MODE_CLASS (mode) == MODE_INT)
+    src = force_to_mode (src, mode, GET_MODE_MASK (mode), NULL_RTX, 0);
+
+  /* Convert this into a field assignment operation, if possible.  */
+  x = make_field_assignment (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.  */
+  if ((GET_CODE (src) == COMPARE
 #ifdef HAVE_cc0
-          || SET_DEST (x) == cc0_rtx
+       || dest == cc0_rtx
 #endif
-          )
-         && (cc_use = find_single_use (SET_DEST (x), subst_insn,
-                                       &other_insn)) != 0
-         && (undobuf.other_insn == 0 || other_insn == undobuf.other_insn)
-         && GET_RTX_CLASS (GET_CODE (*cc_use)) == '<'
-         && XEXP (*cc_use, 0) == SET_DEST (x))
-       {
-         enum rtx_code old_code = GET_CODE (*cc_use);
-         enum rtx_code new_code;
-         rtx op0, op1;
-         int other_changed = 0;
-         enum machine_mode compare_mode = GET_MODE (SET_DEST (x));
+       )
+      && (cc_use = find_single_use (dest, subst_insn, &other_insn)) != 0
+      && (undobuf.other_insn == 0 || other_insn == undobuf.other_insn)
+      && GET_RTX_CLASS (GET_CODE (*cc_use)) == '<'
+      && XEXP (*cc_use, 0) == dest)
+    {
+      enum rtx_code old_code = GET_CODE (*cc_use);
+      enum rtx_code new_code;
+      rtx op0, op1;
+      int other_changed = 0;
+      enum machine_mode compare_mode = GET_MODE (dest);
+
+      if (GET_CODE (src) == COMPARE)
+       op0 = XEXP (src, 0), op1 = XEXP (src, 1);
+      else
+       op0 = src, op1 = const0_rtx;
 
-         if (GET_CODE (SET_SRC (x)) == COMPARE)
-           op0 = XEXP (SET_SRC (x), 0), op1 = XEXP (SET_SRC (x), 1);
-         else
-           op0 = SET_SRC (x), op1 = const0_rtx;
+      /* Simplify our comparison, if possible.  */
+      new_code = simplify_comparison (old_code, &op0, &op1);
 
-         /* Simplify our comparison, if possible.  */
-         new_code = simplify_comparison (old_code, &op0, &op1);
+#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 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);
-
-         /* 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
-            the proper mode.  If it is a pseudo, we lose unless it is only
-            time we set the pseudo, in which case we can safely change
-            its mode.  */
-         if (compare_mode != GET_MODE (SET_DEST (x)))
+      /* 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 the proper mode.  If it
+        is a pseudo, we lose unless it is only time we set the pseudo, in
+        which case we can safely change its mode.  */
+      if (compare_mode != GET_MODE (dest))
+       {
+         int regno = REGNO (dest);
+         rtx new_dest = gen_rtx (REG, compare_mode, regno);
+
+         if (regno < FIRST_PSEUDO_REGISTER
+             || (reg_n_sets[regno] == 1 && ! REG_USERVAR_P (dest)))
            {
-             int regno = REGNO (SET_DEST (x));
-             rtx new_dest = gen_rtx (REG, compare_mode, regno);
+             if (regno >= FIRST_PSEUDO_REGISTER)
+               SUBST (regno_reg_rtx[regno], new_dest);
 
-             if (regno < FIRST_PSEUDO_REGISTER
-                 || (reg_n_sets[regno] == 1
-                     && ! REG_USERVAR_P (SET_DEST (x))))
-               {
-                 if (regno >= FIRST_PSEUDO_REGISTER)
-                   SUBST (regno_reg_rtx[regno], new_dest);
+             SUBST (SET_DEST (x), new_dest);
+             SUBST (XEXP (*cc_use, 0), new_dest);
+             other_changed = 1;
 
-                 SUBST (SET_DEST (x), new_dest);
-                 SUBST (XEXP (*cc_use, 0), new_dest);
-                 other_changed = 1;
-               }
+             dest = new_dest;
            }
+       }
 #endif
 
-         /* If the code changed, we have to build a new comparison
-            in undobuf.other_insn.  */
-         if (new_code != old_code)
+      /* If the code changed, we have to build a new comparison in
+        undobuf.other_insn.  */
+      if (new_code != old_code)
+       {
+         unsigned HOST_WIDE_INT mask;
+
+         SUBST (*cc_use, gen_rtx_combine (new_code, GET_MODE (*cc_use),
+                                          dest, const0_rtx));
+
+         /* If the only change we made was to change an EQ into an 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 with an XOR.  */
+
+         if (((old_code == NE && new_code == EQ)
+              || (old_code == EQ && new_code == NE))
+             && ! other_changed && op1 == const0_rtx
+             && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT
+             && exact_log2 (mask = nonzero_bits (op0, GET_MODE (op0))) >= 0)
            {
-             unsigned HOST_WIDE_INT mask;
+             rtx pat = PATTERN (other_insn), note = 0;
 
-             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 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
-                with an XOR.  */
-
-             if (((old_code == NE && new_code == EQ)
-                  || (old_code == EQ && new_code == NE))
-                 && ! other_changed && op1 == const0_rtx
-                 && (GET_MODE_BITSIZE (GET_MODE (op0))
-                     <= HOST_BITS_PER_WIDE_INT)
-                 && (exact_log2 (mask = nonzero_bits (op0, GET_MODE (op0)))
-                     >= 0))
+             if ((recog_for_combine (&pat, other_insn, &note) < 0
+                  && ! check_asm_operands (pat)))
                {
-                 rtx pat = PATTERN (other_insn), note = 0;
+                 PUT_CODE (*cc_use, old_code);
+                 other_insn = 0;
 
-                 if ((recog_for_combine (&pat, other_insn, &note) < 0
-                      && ! check_asm_operands (pat)))
-                   {
-                     PUT_CODE (*cc_use, old_code);
-                     other_insn = 0;
-
-                     op0 = gen_binary (XOR, GET_MODE (op0), op0,
-                                       GEN_INT (mask));
-                   }
+                 op0 = gen_binary (XOR, GET_MODE (op0), op0, GEN_INT (mask));
                }
-
-             other_changed = 1;
            }
 
-         if (other_changed)
-           undobuf.other_insn = other_insn;
+         other_changed = 1;
+       }
+
+      if (other_changed)
+       undobuf.other_insn = other_insn;
 
 #ifdef HAVE_cc0
-         /* If we are now comparing against zero, change our source if
-            needed.  If we do not use cc0, we always have a COMPARE.  */
-         if (op1 == const0_rtx && SET_DEST (x) == cc0_rtx)
-           SUBST (SET_SRC (x), op0);
-         else
+      /* If we are now comparing against zero, change our source if
+        needed.  If we do not use cc0, we always have a COMPARE.  */
+      if (op1 == const0_rtx && dest == cc0_rtx)
+       {
+         SUBST (SET_SRC (x), op0);
+         src = op0;
+       }
+      else
 #endif
 
-         /* Otherwise, if we didn't previously have a COMPARE in the
-            correct mode, we need one.  */
-         if (GET_CODE (SET_SRC (x)) != COMPARE
-             || GET_MODE (SET_SRC (x)) != compare_mode)
-           SUBST (SET_SRC (x), gen_rtx_combine (COMPARE, compare_mode,
-                                                op0, op1));
-         else
-           {
-             /* Otherwise, update the COMPARE if needed.  */
-             SUBST (XEXP (SET_SRC (x), 0), op0);
-             SUBST (XEXP (SET_SRC (x), 1), op1);
-           }
+      /* Otherwise, if we didn't previously have a COMPARE in the
+        correct mode, we need one.  */
+      if (GET_CODE (src) != COMPARE || GET_MODE (src) != compare_mode)
+       {
+         SUBST (SET_SRC (x),
+                gen_rtx_combine (COMPARE, compare_mode, op0, op1));
+         src = SET_SRC (x);
        }
       else
        {
-         /* Get SET_SRC in a form where we have placed back any
-            compound expressions.  Then do the checks below.  */
-         temp = make_compound_operation (SET_SRC (x), SET);
-         SUBST (SET_SRC (x), temp);
+         /* Otherwise, update the COMPARE if needed.  */
+         SUBST (XEXP (src, 0), op0);
+         SUBST (XEXP (src, 1), op1);
        }
+    }
+  else
+    {
+      /* Get SET_SRC in a form where we have placed back any
+        compound expressions.  Then do the checks below.  */
+      src = make_compound_operation (src, SET);
+      SUBST (SET_SRC (x), src);
+    }
 
-      /* 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)).
+  /* 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.
+     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 BYTES_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.  */
+     However, on machines without WORD_REGISTER_OPERATIONS defined, we cannot
+     perform a narrower operation that requested since the high-order bits will
+     be undefined.  On machine where it is defined, 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)))))
+  if (GET_CODE (src) == SUBREG && subreg_lowpart_p (src)
+      && GET_RTX_CLASS (GET_CODE (SUBREG_REG (src))) != 'o'
+      && (((GET_MODE_SIZE (GET_MODE (src)) + (UNITS_PER_WORD - 1))
+          / UNITS_PER_WORD)
+         == ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))
+              + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD))
+#ifndef WORD_REGISTER_OPERATIONS
+      && (GET_MODE_SIZE (GET_MODE (src))
+         < GET_MODE_SIZE (GET_MODE (SUBREG_REG (src))))
 #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)));
-       }
+      && (GET_CODE (dest) == REG
+         || (GET_CODE (dest) == SUBREG
+             && GET_CODE (SUBREG_REG (dest)) == REG)))
+    {
+      SUBST (SET_DEST (x),
+            gen_lowpart_for_combine (GET_MODE (SUBREG_REG (src)),
+                                     dest));
+      SUBST (SET_SRC (x), SUBREG_REG (src));
 
-#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 (LOAD_EXTEND,
-                                            GET_MODE (SET_SRC (x)),
-                                            XEXP (SET_SRC (x), 0)));
+      src = SET_SRC (x), dest = SET_DEST (x);
+    }
+
+#ifdef LOAD_EXTEND_OP
+  /* 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 (src) == SUBREG && subreg_lowpart_p (src)
+      && LOAD_EXTEND_OP (GET_MODE (SUBREG_REG (src))) != NIL
+      && SUBREG_WORD (src) == 0
+      && (GET_MODE_SIZE (GET_MODE (src))
+         > GET_MODE_SIZE (GET_MODE (SUBREG_REG (src))))
+      && GET_CODE (SUBREG_REG (src)) == MEM)
+    {
+      SUBST (SET_SRC (x),
+            gen_rtx_combine (LOAD_EXTEND_OP (GET_MODE (SUBREG_REG (src))),
+                             GET_MODE (src), XEXP (src, 0)));
+
+      src = SET_SRC (x);
+    }
 #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));
-       }
+  /* 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 (dest) != PC
+      && GET_CODE (src) == IF_THEN_ELSE
+      && (GET_CODE (XEXP (src, 0)) == EQ || GET_CODE (XEXP (src, 0)) == NE)
+      && XEXP (XEXP (src, 0), 1) == const0_rtx
+      && (num_sign_bit_copies (XEXP (XEXP (src, 0), 0),
+                              GET_MODE (XEXP (XEXP (src, 0), 0)))
+         == GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (src, 0), 0))))
+      && ! side_effects_p (src))
+    {
+      rtx true = (GET_CODE (XEXP (src, 0)) == NE
+                     ? XEXP (src, 1) : XEXP (src, 2));
+      rtx false = (GET_CODE (XEXP (src, 0)) == NE
+                  ? XEXP (src, 2) : XEXP (src, 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 (src), XEXP (XEXP (src, 0), 0), true);
+      term3 = gen_binary (AND, GET_MODE (src),
+                         gen_unary (NOT, GET_MODE (src),
+                                    XEXP (XEXP (src, 0), 0)),
+                         false);
+
+      SUBST (SET_SRC (x),
+            gen_binary (IOR, GET_MODE (src),
+                        gen_binary (IOR, GET_MODE (src), term1, term2),
+                        term3));
+
+      src = SET_SRC (x);
+    }
 #endif
-      break;
 
+  return x;
+}
+\f
+/* Simplify, X, and AND, IOR, or XOR operation, and return the simplified
+   result.  LAST is nonzero if this is the last retry.  */
+
+static rtx
+simplify_logical (x, last)
+     rtx x;
+     int last;
+{
+  enum machine_mode mode = GET_MODE (x);
+  rtx op0 = XEXP (x, 0);
+  rtx op1 = XEXP (x, 1);
+
+  switch (GET_CODE (x))
+    {
     case AND:
-      if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+      /* Convert (A ^ B) & A to A & (~ B) since the latter is often a single
+        insn (and may simplify more).  */
+      if (GET_CODE (op0) == XOR
+         && rtx_equal_p (XEXP (op0, 0), op1)
+         && ! side_effects_p (op1))
+       x = gen_binary (AND, mode, gen_unary (NOT, mode, XEXP (op0, 1)), op1);
+
+      if (GET_CODE (op0) == XOR
+         && rtx_equal_p (XEXP (op0, 1), op1)
+         && ! side_effects_p (op1))
+       x = gen_binary (AND, mode, gen_unary (NOT, mode, XEXP (op0, 0)), op1);
+
+      /* Similarly for (~ (A ^ B)) & A.  */
+      if (GET_CODE (op0) == NOT
+         && GET_CODE (XEXP (op0, 0)) == XOR
+         && rtx_equal_p (XEXP (XEXP (op0, 0), 0), op1)
+         && ! side_effects_p (op1))
+       x = gen_binary (AND, mode, XEXP (XEXP (op0, 0), 1), op1);
+
+      if (GET_CODE (op0) == NOT
+         && GET_CODE (XEXP (op0, 0)) == XOR
+         && rtx_equal_p (XEXP (XEXP (op0, 0), 1), op1)
+         && ! side_effects_p (op1))
+       x = gen_binary (AND, mode, XEXP (XEXP (op0, 0), 0), op1);
+
+      if (GET_CODE (op1) == CONST_INT)
        {
-         x = simplify_and_const_int (x, mode, XEXP (x, 0),
-                                     INTVAL (XEXP (x, 1)));
+         x = simplify_and_const_int (x, mode, op0, INTVAL (op1));
 
          /* If we have (ior (and (X C1) C2)) and the next restart would be
             the last, simplify this by making C1 as small as possible
             and then exit. */
-         if (n_restarts >= 3 && GET_CODE (x) == IOR
-             && GET_CODE (XEXP (x, 0)) == AND
-             && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
-             && GET_CODE (XEXP (x, 1)) == CONST_INT)
-           {
-             temp = gen_binary (AND, mode, XEXP (XEXP (x, 0), 0),
-                                GEN_INT (INTVAL (XEXP (XEXP (x, 0), 1))
-                                         & ~ INTVAL (XEXP (x, 1))));
-             return gen_binary (IOR, mode, temp, XEXP (x, 1));
-           }
+         if (last
+             && GET_CODE (x) == IOR && GET_CODE (op0) == AND
+             && GET_CODE (XEXP (op0, 1)) == CONST_INT
+             && GET_CODE (op1) == CONST_INT)
+           return gen_binary (IOR, mode,
+                              gen_binary (AND, mode, XEXP (op0, 0),
+                                          GEN_INT (INTVAL (XEXP (op0, 1))
+                                                   & ~ INTVAL (op1))), op1);
 
          if (GET_CODE (x) != AND)
-           goto restart;
+           return x;
        }
 
       /* Convert (A | B) & A to A.  */
-      if (GET_CODE (XEXP (x, 0)) == IOR
-         && (rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1))
-             || rtx_equal_p (XEXP (XEXP (x, 0), 1), XEXP (x, 1)))
-         && ! side_effects_p (XEXP (XEXP (x, 0), 0))
-         && ! side_effects_p (XEXP (XEXP (x, 0), 1)))
-       return XEXP (x, 1);
-
-      /* Convert (A ^ B) & A to A & (~ B) since the latter is often a single
-        insn (and may simplify more).  */
-      else if (GET_CODE (XEXP (x, 0)) == XOR
-         && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1))
-         && ! side_effects_p (XEXP (x, 1)))
-       {
-         x = gen_binary (AND, mode,
-                         gen_unary (NOT, mode, XEXP (XEXP (x, 0), 1)),
-                         XEXP (x, 1));
-         goto restart;
-       }
-      else if (GET_CODE (XEXP (x, 0)) == XOR
-              && rtx_equal_p (XEXP (XEXP (x, 0), 1), XEXP (x, 1))
-              && ! side_effects_p (XEXP (x, 1)))
-       {
-         x = gen_binary (AND, mode,
-                         gen_unary (NOT, mode, XEXP (XEXP (x, 0), 0)),
-                         XEXP (x, 1));
-         goto restart;
-       }
-
-      /* Similarly for (~ (A ^ B)) & A.  */
-      else if (GET_CODE (XEXP (x, 0)) == NOT
-              && GET_CODE (XEXP (XEXP (x, 0), 0)) == XOR
-              && rtx_equal_p (XEXP (XEXP (XEXP (x, 0), 0), 0), XEXP (x, 1))
-              && ! side_effects_p (XEXP (x, 1)))
-       {
-         x = gen_binary (AND, mode, XEXP (XEXP (XEXP (x, 0), 0), 1),
-                         XEXP (x, 1));
-         goto restart;
-       }
-      else if (GET_CODE (XEXP (x, 0)) == NOT
-              && GET_CODE (XEXP (XEXP (x, 0), 0)) == XOR
-              && rtx_equal_p (XEXP (XEXP (XEXP (x, 0), 0), 1), XEXP (x, 1))
-              && ! side_effects_p (XEXP (x, 1)))
-       {
-         x = gen_binary (AND, mode, XEXP (XEXP (XEXP (x, 0), 0), 0),
-                         XEXP (x, 1));
-         goto restart;
-       }
-
-      /* 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;
-       }
+      if (GET_CODE (op0) == IOR
+         && (rtx_equal_p (XEXP (op0, 0), op1)
+             || rtx_equal_p (XEXP (op0, 1), op1))
+         && ! side_effects_p (XEXP (op0, 0))
+         && ! side_effects_p (XEXP (op0, 1)))
+       return op1;
 
       /* In the following group of tests (and those in case IOR below),
         we start with some combination of logical operations and apply
@@ -4132,128 +4516,118 @@ subst (x, from, to, in_dest, unique_copy)
         For example, (and (ior A B) (not B)) can occur as the result of
         expanding a bit field assignment.  When we apply the distributive
         law to this, we get (ior (and (A (not B))) (and (B (not B)))),
-        which then simplifies to (and (A (not B))).  */
+        which then simplifies to (and (A (not B))). 
 
-      /* If we have (and (ior A B) C), apply the distributive law and then
+        If we have (and (ior A B) C), apply the distributive law and then
         the inverse distributive law to see if things simplify.  */
 
-      if (GET_CODE (XEXP (x, 0)) == IOR || GET_CODE (XEXP (x, 0)) == XOR)
+      if (GET_CODE (op0) == IOR || GET_CODE (op0) == XOR)
        {
          x = apply_distributive_law
-           (gen_binary (GET_CODE (XEXP (x, 0)), mode,
-                        gen_binary (AND, mode,
-                                    XEXP (XEXP (x, 0), 0), XEXP (x, 1)),
-                        gen_binary (AND, mode,
-                                    XEXP (XEXP (x, 0), 1), XEXP (x, 1))));
+           (gen_binary (GET_CODE (op0), mode,
+                        gen_binary (AND, mode, XEXP (op0, 0), op1),
+                        gen_binary (AND, mode, XEXP (op0, 1), op1)));
          if (GET_CODE (x) != AND)
-           goto restart;
+           return x;
        }
 
-      if (GET_CODE (XEXP (x, 1)) == IOR || GET_CODE (XEXP (x, 1)) == XOR)
-       {
-         x = apply_distributive_law
-           (gen_binary (GET_CODE (XEXP (x, 1)), mode,
-                        gen_binary (AND, mode,
-                                    XEXP (XEXP (x, 1), 0), XEXP (x, 0)),
-                        gen_binary (AND, mode,
-                                    XEXP (XEXP (x, 1), 1), XEXP (x, 0))));
-         if (GET_CODE (x) != AND)
-           goto restart;
-       }
+      if (GET_CODE (op1) == IOR || GET_CODE (op1) == XOR)
+       return apply_distributive_law
+         (gen_binary (GET_CODE (op1), mode,
+                      gen_binary (AND, mode, XEXP (op1, 0), op0),
+                      gen_binary (AND, mode, XEXP (op1, 1), op0)));
 
       /* Similarly, taking advantage of the fact that
         (and (not A) (xor B C)) == (xor (ior A B) (ior A C))  */
 
-      if (GET_CODE (XEXP (x, 0)) == NOT && GET_CODE (XEXP (x, 1)) == XOR)
-       {
-         x = apply_distributive_law
-           (gen_binary (XOR, mode,
-                        gen_binary (IOR, mode, XEXP (XEXP (x, 0), 0),
-                                    XEXP (XEXP (x, 1), 0)),
-                        gen_binary (IOR, mode, XEXP (XEXP (x, 0), 0),
-                                    XEXP (XEXP (x, 1), 1))));
-         if (GET_CODE (x) != AND)
-           goto restart;
-       }
+      if (GET_CODE (op0) == NOT && GET_CODE (op1) == XOR)
+       return apply_distributive_law
+         (gen_binary (XOR, mode,
+                      gen_binary (IOR, mode, XEXP (op0, 0), XEXP (op1, 0)),
+                      gen_binary (IOR, mode, XEXP (op0, 0), XEXP (op1, 1))));
                                                            
-      else if (GET_CODE (XEXP (x, 1)) == NOT && GET_CODE (XEXP (x, 0)) == XOR)
-       {
-         x = apply_distributive_law
-           (gen_binary (XOR, mode,
-                        gen_binary (IOR, mode, XEXP (XEXP (x, 1), 0),
-                                    XEXP (XEXP (x, 0), 0)),
-                        gen_binary (IOR, mode, XEXP (XEXP (x, 1), 0),
-                                    XEXP (XEXP (x, 0), 1))));
-         if (GET_CODE (x) != AND)
-           goto restart;
-       }
+      else if (GET_CODE (op1) == NOT && GET_CODE (op0) == XOR)
+       return apply_distributive_law
+         (gen_binary (XOR, mode,
+                      gen_binary (IOR, mode, XEXP (op1, 0), XEXP (op0, 0)),
+                      gen_binary (IOR, mode, XEXP (op1, 0), XEXP (op0, 1))));
       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
+      if (GET_CODE (op1) == 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);
+         && (nonzero_bits (op0, mode) & ~ INTVAL (op1)) == 0)
+       return op1;
 
       /* Convert (A & B) | A to A.  */
-      if (GET_CODE (XEXP (x, 0)) == AND
-         && (rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1))
-             || rtx_equal_p (XEXP (XEXP (x, 0), 1), XEXP (x, 1)))
-         && ! side_effects_p (XEXP (XEXP (x, 0), 0))
-         && ! side_effects_p (XEXP (XEXP (x, 0), 1)))
-       return XEXP (x, 1);
+      if (GET_CODE (op0) == AND
+         && (rtx_equal_p (XEXP (op0, 0), op1)
+             || rtx_equal_p (XEXP (op0, 1), op1))
+         && ! side_effects_p (XEXP (op0, 0))
+         && ! side_effects_p (XEXP (op0, 1)))
+       return op1;
 
       /* If we have (ior (and A B) C), apply the distributive law and then
         the inverse distributive law to see if things simplify.  */
 
-      if (GET_CODE (XEXP (x, 0)) == AND)
+      if (GET_CODE (op0) == AND)
        {
          x = apply_distributive_law
            (gen_binary (AND, mode,
-                        gen_binary (IOR, mode,
-                                    XEXP (XEXP (x, 0), 0), XEXP (x, 1)),
-                        gen_binary (IOR, mode,
-                                    XEXP (XEXP (x, 0), 1), XEXP (x, 1))));
+                        gen_binary (IOR, mode, XEXP (op0, 0), op1),
+                        gen_binary (IOR, mode, XEXP (op0, 1), op1)));
 
          if (GET_CODE (x) != IOR)
-           goto restart;
+           return x;
        }
 
-      if (GET_CODE (XEXP (x, 1)) == AND)
+      if (GET_CODE (op1) == AND)
        {
          x = apply_distributive_law
            (gen_binary (AND, mode,
-                        gen_binary (IOR, mode,
-                                    XEXP (XEXP (x, 1), 0), XEXP (x, 0)),
-                        gen_binary (IOR, mode,
-                                    XEXP (XEXP (x, 1), 1), XEXP (x, 0))));
+                        gen_binary (IOR, mode, XEXP (op1, 0), op0),
+                        gen_binary (IOR, mode, XEXP (op1, 1), op0)));
 
          if (GET_CODE (x) != IOR)
-           goto restart;
+           return x;
        }
 
       /* Convert (ior (ashift A CX) (lshiftrt A CY)) where CX+CY equals the
         mode size to (rotate A CX).  */
 
-      if (((GET_CODE (XEXP (x, 0)) == ASHIFT
-           && GET_CODE (XEXP (x, 1)) == LSHIFTRT)
-          || (GET_CODE (XEXP (x, 1)) == ASHIFT
-              && GET_CODE (XEXP (x, 0)) == LSHIFTRT))
-         && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (XEXP (x, 1), 0))
-         && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
-         && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT
-         && (INTVAL (XEXP (XEXP (x, 0), 1)) + INTVAL (XEXP (XEXP (x, 1), 1))
+      if (((GET_CODE (op0) == ASHIFT && GET_CODE (op1) == LSHIFTRT)
+          || (GET_CODE (op1) == ASHIFT && GET_CODE (op0) == LSHIFTRT))
+         && rtx_equal_p (XEXP (op0, 0), XEXP (op1, 0))
+         && GET_CODE (XEXP (op0, 1)) == CONST_INT
+         && GET_CODE (XEXP (op1, 1)) == CONST_INT
+         && (INTVAL (XEXP (op0, 1)) + INTVAL (XEXP (op1, 1))
              == GET_MODE_BITSIZE (mode)))
+       return gen_rtx (ROTATE, mode, XEXP (op0, 0),
+                       (GET_CODE (op0) == ASHIFT
+                        ? XEXP (op0, 1) : XEXP (op1, 1)));
+
+      /* If OP0 is (ashiftrt (plus ...) C), it might actually be
+        a (sign_extend (plus ...)).  If so, OP1 is a CONST_INT, and the PLUS
+        does not affect any of the bits in OP1, it can really be done
+        as a PLUS and we can associate.  We do this by seeing if OP1
+        can be safely shifted left C bits.  */
+      if (GET_CODE (op1) == CONST_INT && GET_CODE (op0) == ASHIFTRT
+         && GET_CODE (XEXP (op0, 0)) == PLUS
+         && GET_CODE (XEXP (XEXP (op0, 0), 1)) == CONST_INT
+         && GET_CODE (XEXP (op0, 1)) == CONST_INT
+         && INTVAL (XEXP (op0, 1)) < HOST_BITS_PER_WIDE_INT)
        {
-         rtx shift_count;
+         int count = INTVAL (XEXP (op0, 1));
+         HOST_WIDE_INT mask = INTVAL (op1) << count;
 
-         if (GET_CODE (XEXP (x, 0)) == ASHIFT)
-           shift_count = XEXP (XEXP (x, 0), 1);
-         else
-           shift_count = XEXP (XEXP (x, 1), 1);
-         x = gen_rtx (ROTATE, mode, XEXP (XEXP (x, 0), 0), shift_count);
-         goto restart;
+         if (mask >> count == INTVAL (op1)
+             && (mask & nonzero_bits (XEXP (op0, 0), mode)) == 0)
+           {
+             SUBST (XEXP (XEXP (op0, 0), 1),
+                    GEN_INT (INTVAL (XEXP (XEXP (op0, 0), 1)) | mask));
+             return op0;
+           }
        }
       break;
 
@@ -4263,144 +4637,67 @@ subst (x, from, to, in_dest, unique_copy)
         (NOT y).  */
       {
        int num_negated = 0;
-       rtx in1 = XEXP (x, 0), in2 = XEXP (x, 1);
 
-       if (GET_CODE (in1) == NOT)
-         num_negated++, in1 = XEXP (in1, 0);
-       if (GET_CODE (in2) == NOT)
-         num_negated++, in2 = XEXP (in2, 0);
+       if (GET_CODE (op0) == NOT)
+         num_negated++, op0 = XEXP (op0, 0);
+       if (GET_CODE (op1) == NOT)
+         num_negated++, op1 = XEXP (op1, 0);
 
        if (num_negated == 2)
          {
-           SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0));
-           SUBST (XEXP (x, 1), XEXP (XEXP (x, 1), 0));
+           SUBST (XEXP (x, 0), op0);
+           SUBST (XEXP (x, 1), op1);
          }
        else if (num_negated == 1)
-         {
-           x =  gen_unary (NOT, mode,
-                           gen_binary (XOR, mode, in1, in2));
-           goto restart;
-         }
+         return gen_unary (NOT, mode, gen_binary (XOR, mode, op0, op1));
       }
 
       /* Convert (xor (and A B) B) to (and (not A) B).  The latter may
         correspond to a machine insn or result in further simplifications
         if B is a constant.  */
 
-      if (GET_CODE (XEXP (x, 0)) == AND
-         && rtx_equal_p (XEXP (XEXP (x, 0), 1), XEXP (x, 1))
-         && ! side_effects_p (XEXP (x, 1)))
-       {
-         x = gen_binary (AND, mode,
-                         gen_unary (NOT, mode, XEXP (XEXP (x, 0), 0)),
-                         XEXP (x, 1));
-         goto restart;
-       }
-      else if (GET_CODE (XEXP (x, 0)) == AND
-              && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1))
-              && ! side_effects_p (XEXP (x, 1)))
-       {
-         x = gen_binary (AND, mode,
-                         gen_unary (NOT, mode, XEXP (XEXP (x, 0), 1)),
-                         XEXP (x, 1));
-         goto restart;
-       }
-
-
-#if STORE_FLAG_VALUE == 1
-      /* (xor (comparison foo bar) (const_int 1)) can become the reversed
-        comparison.  */
-      if (XEXP (x, 1) == const1_rtx
-         && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
-         && reversible_comparison_p (XEXP (x, 0)))
-       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))
-        when STORE_FLAG_VALUE is the sign bit.  */
-      if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
-         && (STORE_FLAG_VALUE
-             == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))
-         && XEXP (x, 1) == const_true_rtx
-         && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
-         && reversible_comparison_p (XEXP (x, 0)))
-       return gen_rtx_combine (reverse_condition (GET_CODE (XEXP (x, 0))),
-                               mode, XEXP (XEXP (x, 0), 0),
-                               XEXP (XEXP (x, 0), 1));
-      break;
-
-    case ABS:
-      /* (abs (neg <foo>)) -> (abs <foo>) */
-      if (GET_CODE (XEXP (x, 0)) == NEG)
-       SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0));
-
-      /* If operand is something known to be positive, ignore the ABS.  */
-      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)
-             && ((nonzero_bits (XEXP (x, 0), GET_MODE (XEXP (x, 0)))
-                  & ((HOST_WIDE_INT) 1
-                     << (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) - 1)))
-                 == 0)))
-       return XEXP (x, 0);
-
-
-      /* If operand is known to be only -1 or 0, convert ABS to NEG.  */
-      if (num_sign_bit_copies (XEXP (x, 0), mode) == GET_MODE_BITSIZE (mode))
-       {
-         x = gen_rtx_combine (NEG, mode, XEXP (x, 0));
-         goto restart;
-       }
-      break;
-
-    case FFS:
-      /* (ffs (*_extend <X>)) = (ffs <X>) */
-      if (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND
-         || GET_CODE (XEXP (x, 0)) == ZERO_EXTEND)
-       SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0));
-      break;
-
-    case FLOAT:
-      /* (float (sign_extend <X>)) = (float <X>).  */
-      if (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND)
-       SUBST (XEXP (x, 0), XEXP (XEXP (x, 0), 0));
-      break;
-
-    case LSHIFT:
-    case ASHIFT:
-    case LSHIFTRT:
-    case ASHIFTRT:
-    case ROTATE:
-    case ROTATERT:
-      /* If this is a shift by a constant amount, simplify it.  */
-      if (GET_CODE (XEXP (x, 1)) == CONST_INT)
-       {
-         x = simplify_shift_const (x, code, mode, XEXP (x, 0), 
-                                   INTVAL (XEXP (x, 1)));
-         if (GET_CODE (x) != code)
-           goto restart;
-       }
+      if (GET_CODE (op0) == AND
+         && rtx_equal_p (XEXP (op0, 1), op1)
+         && ! side_effects_p (op1))
+       return gen_binary (AND, mode, gen_unary (NOT, mode, XEXP (op0, 0)),
+                          op1);
 
-#ifdef SHIFT_COUNT_TRUNCATED
-      else if (GET_CODE (XEXP (x, 1)) != REG)
-       SUBST (XEXP (x, 1),
-              force_to_mode (XEXP (x, 1), GET_MODE (x),
-                             exact_log2 (GET_MODE_BITSIZE (GET_MODE (x))),
-                             NULL_RTX));
+      else if (GET_CODE (op0) == AND
+              && rtx_equal_p (XEXP (op0, 0), op1)
+              && ! side_effects_p (op1))
+       return gen_binary (AND, mode, gen_unary (NOT, mode, XEXP (op0, 1)),
+                          op1);
+
+#if STORE_FLAG_VALUE == 1
+      /* (xor (comparison foo bar) (const_int 1)) can become the reversed
+        comparison.  */
+      if (op1 == const1_rtx
+         && GET_RTX_CLASS (GET_CODE (op0)) == '<'
+         && reversible_comparison_p (op0))
+       return gen_rtx_combine (reverse_condition (GET_CODE (op0)),
+                               mode, XEXP (op0, 0), XEXP (op0, 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 (op1 == const1_rtx
+         && GET_CODE (op0) == LSHIFTRT
+         && GET_CODE (XEXP (op0, 1)) == CONST_INT
+         && INTVAL (XEXP (op0, 1)) == GET_MODE_BITSIZE (mode) - 1)
+       return gen_rtx_combine (GE, mode, XEXP (op0, 0), const0_rtx);
 #endif
 
+      /* (xor (comparison foo bar) (const_int sign-bit))
+        when STORE_FLAG_VALUE is the sign bit.  */
+      if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+         && (STORE_FLAG_VALUE
+             == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))
+         && op1 == const_true_rtx
+         && GET_RTX_CLASS (GET_CODE (op0)) == '<'
+         && reversible_comparison_p (op0))
+       return gen_rtx_combine (reverse_condition (GET_CODE (op0)),
+                               mode, XEXP (op0, 0), XEXP (op0, 1));
       break;
     }
 
@@ -4445,7 +4742,16 @@ expand_compound_operation (x)
       if (GET_CODE (XEXP (x, 0)) == CONST_INT)
        return x;
 
-      if (! FAKE_EXTEND_SAFE_P (GET_MODE (XEXP (x, 0)), XEXP (x, 0)))
+      /* Return if (subreg:MODE FROM 0) is not a safe replacement for
+        (zero_extend:MODE FROM) or (sign_extend:MODE FROM).  It is for any MEM
+        because (SUBREG (MEM...)) is guaranteed to cause the MEM to be
+        reloaded. If not for that, MEM's would very rarely be safe.
+
+        Reject MODEs bigger than a word, because we might not be able
+        to reference a two-register group starting with an arbitrary register
+        (and currently gen_lowpart might crash for a SUBREG).  */
+  
+      if (GET_MODE_SIZE (GET_MODE (XEXP (x, 0))) > UNITS_PER_WORD)
        return x;
 
       len = GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)));
@@ -4687,6 +4993,7 @@ make_extraction (mode, inner, pos, pos_rtx, len,
   int spans_byte = 0;
   rtx new = 0;
   rtx orig_pos_rtx = pos_rtx;
+  int orig_pos;
 
   /* Get some information about INNER and get the innermost object.  */
   if (GET_CODE (inner) == USE)
@@ -4773,7 +5080,11 @@ make_extraction (mode, inner, pos, pos_rtx, len,
                           / UNITS_PER_WORD)
                        : 0));
       else
-       new = force_to_mode (inner, tmode, len, NULL_RTX);
+       new = force_to_mode (inner, tmode,
+                            len >= HOST_BITS_PER_WIDE_INT
+                            ? GET_MODE_MASK (tmode)
+                            : ((HOST_WIDE_INT) 1 << len) - 1,
+                            NULL_RTX, 0);
 
       /* If this extraction is going into the destination of a SET, 
         make a STRICT_LOW_PART unless we made a MEM.  */
@@ -4799,6 +5110,16 @@ make_extraction (mode, inner, pos, pos_rtx, len,
       && ! in_compare && ! spans_byte && unsignedp)
     return 0;
 
+  /* Unless we are allowed to span bytes, reject this if we would be
+     spanning bytes or if the position is not a constant and the length
+     is not 1.  In all other cases, we would only be going outside
+     out object in cases when an original shift would have been
+     undefined.  */
+  if (! spans_byte
+      && ((pos_rtx == 0 && pos + len > GET_MODE_BITSIZE (is_mode))
+         || (pos_rtx != 0 && len != 1)))
+    return 0;
+
   /* Get the mode to use should INNER be a MEM, the mode for the position,
      and the mode for the result.  */
 #ifdef HAVE_insv
@@ -4846,6 +5167,8 @@ make_extraction (mode, inner, pos, pos_rtx, len,
              || MEM_VOLATILE_P (inner))))
     wanted_mem_mode = extraction_mode;
 
+  orig_pos = pos;
+
 #if BITS_BIG_ENDIAN
   /* If position is constant, compute new position.  Otherwise, build
      subtraction.  */
@@ -4877,12 +5200,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
@@ -4898,6 +5215,12 @@ make_extraction (mode, inner, pos, pos_rtx, len,
          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,
@@ -4912,9 +5235,10 @@ 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 = force_to_mode (inner, extraction_mode,
-                          (pos < 0 ? GET_MODE_BITSIZE (extraction_mode)
-                           : len + pos),
-                          NULL_RTX);
+                          pos_rtx || len + orig_pos >= HOST_BITS_PER_WIDE_INT
+                          ? GET_MODE_MASK (extraction_mode)
+                          : (((HOST_WIDE_INT) 1 << len) - 1) << orig_pos,
+                          NULL_RTX, 0);
 
   /* 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.  */
@@ -4943,6 +5267,51 @@ make_extraction (mode, inner, pos, pos_rtx, len,
   return new;
 }
 \f
+/* See if X contains an ASHIFT of COUNT or more bits that can be commuted
+   with any other operations in X.  Return X without that shift if so.  */
+
+static rtx
+extract_left_shift (x, count)
+     rtx x;
+     int count;
+{
+  enum rtx_code code = GET_CODE (x);
+  enum machine_mode mode = GET_MODE (x);
+  rtx tem;
+
+  switch (code)
+    {
+    case ASHIFT:
+      /* This is the shift itself.  If it is wide enough, we will return
+        either the value being shifted if the shift count is equal to
+        COUNT or a shift for the difference.  */
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+         && INTVAL (XEXP (x, 1)) >= count)
+       return simplify_shift_const (NULL_RTX, ASHIFT, mode, XEXP (x, 0),
+                                    INTVAL (XEXP (x, 1)) - count);
+      break;
+
+    case NEG:  case NOT:
+      if ((tem = extract_left_shift (XEXP (x, 0), count)) != 0)
+       return gen_unary (code, mode, tem);
+
+      break;
+
+    case PLUS:  case IOR:  case XOR:  case AND:
+      /* If we can safely shift this constant and we find the inner shift,
+        make a new operation.  */
+      if (GET_CODE (XEXP (x,1)) == CONST_INT
+         && (INTVAL (XEXP (x, 1)) & (((HOST_WIDE_INT) 1 << count)) - 1) == 0
+         && (tem = extract_left_shift (XEXP (x, 0), count)) != 0)
+       return gen_binary (code, mode, tem, 
+                          GEN_INT (INTVAL (XEXP (x, 1)) >> count));
+
+      break;
+    }
+
+  return 0;
+}
+\f
 /* Look at the expression rooted at X.  Look for expressions
    equivalent to ZERO_EXTRACT, SIGN_EXTRACT, ZERO_EXTEND, SIGN_EXTEND.
    Form these expressions.
@@ -4969,8 +5338,9 @@ make_compound_operation (x, in_code)
   enum rtx_code code = GET_CODE (x);
   enum machine_mode mode = GET_MODE (x);
   int mode_width = GET_MODE_BITSIZE (mode);
+  rtx rhs, lhs;
   enum rtx_code next_code;
-  int i, count;
+  int i;
   rtx new = 0;
   rtx tem;
   char *fmt;
@@ -4990,7 +5360,6 @@ make_compound_operation (x, in_code)
   switch (code)
     {
     case ASHIFT:
-    case LSHIFT:
       /* Convert shifts by constants into multiplications if inside
         an address.  */
       if (in_code == MEM && GET_CODE (XEXP (x, 1)) == CONST_INT
@@ -5032,6 +5401,21 @@ make_compound_operation (x, in_code)
                                 XEXP (SUBREG_REG (XEXP (x, 0)), 1), i, 1,
                                 0, in_code == COMPARE);
        }
+      /* Same as previous, but for (xor/ior (lshiftrt...) (lshiftrt...)).  */
+      else if ((GET_CODE (XEXP (x, 0)) == XOR
+               || GET_CODE (XEXP (x, 0)) == IOR)
+              && GET_CODE (XEXP (XEXP (x, 0), 0)) == LSHIFTRT
+              && GET_CODE (XEXP (XEXP (x, 0), 1)) == LSHIFTRT
+              && (i = exact_log2 (INTVAL (XEXP (x, 1)) + 1)) >= 0)
+       {
+         /* Apply the distributive law, and then try to make extractions.  */
+         new = gen_rtx_combine (GET_CODE (XEXP (x, 0)), mode,
+                                gen_rtx (AND, mode, XEXP (XEXP (x, 0), 0),
+                                         XEXP (x, 1)),
+                                gen_rtx (AND, mode, XEXP (XEXP (x, 0), 1),
+                                         XEXP (x, 1)));
+         new = make_compound_operation (new, in_code);
+       }
 
       /* If we are have (and (rotate X C) M) and C is larger than the number
         of bits in M, this is an extraction.  */
@@ -5110,82 +5494,38 @@ make_compound_operation (x, in_code)
       /* ... fall through ... */
 
     case ASHIFTRT:
+      lhs = XEXP (x, 0);
+      rhs = XEXP (x, 1);
+
       /* If we have (ashiftrt (ashift foo C1) C2) with C2 >= C1,
         this is a SIGN_EXTRACT.  */
-      if (GET_CODE (XEXP (x, 1)) == CONST_INT
-         && 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)))
+      if (GET_CODE (rhs) == CONST_INT
+         && GET_CODE (lhs) == ASHIFT
+         && GET_CODE (XEXP (lhs, 1)) == CONST_INT
+         && INTVAL (rhs) >= INTVAL (XEXP (lhs, 1)))
        {
-         new = make_compound_operation (XEXP (XEXP (x, 0), 0), next_code);
+         new = make_compound_operation (XEXP (lhs, 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 (c1),
+                                INTVAL (rhs) - INTVAL (XEXP (lhs, 1)),
+                                NULL_RTX, mode_width - INTVAL (rhs),
                                 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);
-       }
+      /* See if we have operations between an ASHIFTRT and an ASHIFT.
+        If so, try to merge the shifts into a SIGN_EXTEND.  We could
+        also do this for some cases of SIGN_EXTRACT, but it doesn't
+        seem worth the effort; the case checked for occurs on Alpha.  */
+      
+      if (GET_RTX_CLASS (GET_CODE (lhs)) != 'o'
+         && ! (GET_CODE (lhs) == SUBREG
+               && (GET_RTX_CLASS (GET_CODE (SUBREG_REG (lhs))) == 'o'))
+         && GET_CODE (rhs) == CONST_INT
+         && INTVAL (rhs) < HOST_BITS_PER_WIDE_INT
+         && (new = extract_left_shift (lhs, INTVAL (rhs))) != 0)
+       new = make_extraction (mode, make_compound_operation (new, next_code),
+                              0, NULL_RTX, mode_width - INTVAL (rhs),
+                              code == LSHIFTRT, 0, in_code == COMPARE);
+       
       break;
 
     case SUBREG:
@@ -5199,7 +5539,7 @@ make_compound_operation (x, in_code)
          && subreg_lowpart_p (x))
        {
          rtx newer = force_to_mode (tem, mode,
-                                    GET_MODE_BITSIZE (mode), NULL_RTX);
+                                    GET_MODE_MASK (mode), NULL_RTX, 0);
 
          /* If we have something other than a SUBREG, we might have
             done an expansion, so rerun outselves.  */
@@ -5256,39 +5596,119 @@ get_pos_from_mask (m, plen)
   return pos;
 }
 \f
-/* Rewrite X so that it is an expression in MODE.  We only care about the
-   low-order BITS bits so we can ignore AND operations that just clear
-   higher-order bits.
+/* See if X can be simplified knowing that we will only refer to it in
+   MODE and will only refer to those bits that are nonzero in MASK.
+   If other bits are being computed or if masking operations are done
+   that select a superset of the bits in MASK, they can sometimes be
+   ignored.
+
+   Return a possibly simplified expression, but always convert X to
+   MODE.  If X is a CONST_INT, AND the CONST_INT with MASK.
 
    Also, if REG is non-zero and X is a register equal in value to REG, 
-   replace X with REG.  */
+   replace X with REG.
+
+   If JUST_SELECT is nonzero, don't optimize by noticing that bits in MASK
+   are all off in X.  This is used when X will be complemented, by either
+   NOT or XOR.  */
 
 static rtx
-force_to_mode (x, mode, bits, reg)
+force_to_mode (x, mode, mask, reg, just_select)
      rtx x;
      enum machine_mode mode;
-     int bits;
+     unsigned HOST_WIDE_INT mask;
      rtx reg;
+     int just_select;
 {
   enum rtx_code code = GET_CODE (x);
-  enum machine_mode op_mode = mode;
+  int next_select = just_select || code == XOR || code == NOT;
+  enum machine_mode op_mode;
+  unsigned HOST_WIDE_INT fuller_mask, nonzero;
+  rtx op0, op1, temp;
+
+  /* We want to perform the operation is its present mode unless we know
+     that the operation is valid in MODE, in which case we do the operation
+     in MODE.  */
+  op_mode = ((code_to_optab[(int) code] != 0
+             && (code_to_optab[(int) code]->handlers[(int) mode].insn_code
+                 != CODE_FOR_nothing))
+            ? mode : GET_MODE (x));
+
+  /* It is not valid to do a right-shift in a narrower mode
+     than the one it came in with.  */
+  if ((code == LSHIFTRT || code == ASHIFTRT)
+      && GET_MODE_BITSIZE (mode) < GET_MODE_BITSIZE (GET_MODE (x)))
+    op_mode = GET_MODE (x);
+
+  /* Truncate MASK to fit OP_MODE.  */
+  if (op_mode)
+    mask &= GET_MODE_MASK (op_mode);
+
+  /* When we have an arithmetic operation, or a shift whose count we
+     do not know, we need to assume that all bit the up to the highest-order
+     bit in MASK will be needed.  This is how we form such a mask.  */
+  if (op_mode)
+    fuller_mask = (GET_MODE_BITSIZE (op_mode) >= HOST_BITS_PER_WIDE_INT
+                  ? GET_MODE_MASK (op_mode)
+                  : ((HOST_WIDE_INT) 1 << (floor_log2 (mask) + 1)) - 1);
+  else
+    fuller_mask = ~ (HOST_WIDE_INT) 0;
+
+  /* Determine what bits of X are guaranteed to be (non)zero.  */
+  nonzero = nonzero_bits (x, mode);
+
+  /* If none of the bits in X are needed, return a zero.  */
+  if (! just_select && (nonzero & mask) == 0)
+    return const0_rtx;
 
-  /* If X is narrower than MODE or if BITS is larger than the size of MODE,
-     just get X in the proper mode.  */
+  /* If X is a CONST_INT, return a new one.  Do this here since the
+     test below will fail.  */
+  if (GET_CODE (x) == CONST_INT)
+    {
+      HOST_WIDE_INT cval = INTVAL (x) & mask;
+      int width = GET_MODE_BITSIZE (mode);
+
+      /* If MODE is narrower that HOST_WIDE_INT and CVAL is a negative
+        number, sign extend it.  */
+      if (width > 0 && width < HOST_BITS_PER_WIDE_INT
+         && (cval & ((HOST_WIDE_INT) 1 << (width - 1))) != 0)
+       cval |= (HOST_WIDE_INT) -1 << width;
+       
+      return GEN_INT (cval);
+    }
 
-  if (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (mode)
-      || bits > GET_MODE_BITSIZE (mode))
+  /* If X is narrower than MODE, just get X in the proper mode.  */
+  if (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (mode))
     return gen_lowpart_for_combine (mode, x);
 
+  /* If we aren't changing the mode, X is not a SUBREG, and all zero bits in
+     MASK are already known to be zero in X, we need not do anything.  */
+  if (GET_MODE (x) == mode && code != SUBREG && (~ mask & nonzero) == 0)
+    return x;
+
   switch (code)
     {
+    case CLOBBER:
+      /* If X is a (clobber (const_int)), return it since we know we are
+        generating something that won't match. */
+      return x;
+
+#if ! BITS_BIG_ENDIAN
+    case USE:
+      /* X is a (use (mem ..)) that was made from a bit-field extraction that
+        spanned the boundary of the MEM.  If we are now masking so it is
+        within that boundary, we don't need the USE any more.  */
+      if ((mask & ~ GET_MODE_MASK (GET_MODE (XEXP (x, 0)))) == 0)
+       return force_to_mode (XEXP (x, 0), mode, mask, reg, next_select);
+#endif
+
     case SIGN_EXTEND:
     case ZERO_EXTEND:
     case ZERO_EXTRACT:
     case SIGN_EXTRACT:
       x = expand_compound_operation (x);
       if (GET_CODE (x) != code)
-       return force_to_mode (x, mode, bits, reg);
+       return force_to_mode (x, mode, mask, reg, next_select);
       break;
 
     case REG:
@@ -5297,192 +5717,530 @@ force_to_mode (x, mode, bits, reg)
        x = reg;
       break;
 
-    case CONST_INT:
-      if (bits < HOST_BITS_PER_WIDE_INT)
-       x = GEN_INT (INTVAL (x) & (((HOST_WIDE_INT) 1 << bits) - 1));
-      return x;
-
     case SUBREG:
-      /* Ignore low-order SUBREGs. */
-      if (subreg_lowpart_p (x))
-       return force_to_mode (SUBREG_REG (x), mode, bits, reg);
+      if (subreg_lowpart_p (x)
+         /* We can ignore the effect this SUBREG if it narrows the mode or,
+            on machines where register operations are performed on the full
+            word, if the constant masks to zero all the bits the mode
+            doesn't have.  */
+         && ((GET_MODE_SIZE (GET_MODE (x))
+              < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+#ifdef WORD_REGISTER_OPERATIONS
+             || (0 == (mask
+                       & GET_MODE_MASK (GET_MODE (x))
+                       & ~ GET_MODE_MASK (GET_MODE (SUBREG_REG (x)))))
+#endif
+             ))
+       return force_to_mode (SUBREG_REG (x), mode, mask, reg, next_select);
       break;
 
     case AND:
-      /* If this is an AND with a constant.  Otherwise, we fall through to
-        do the general binary case.  */
+      /* If this is an AND with a constant, convert it into an AND
+        whose constant is the AND of that constant with MASK.  If it
+        remains an AND of MASK, delete it since it is redundant.  */
 
-      if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+         && GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT)
        {
-         HOST_WIDE_INT mask = INTVAL (XEXP (x, 1));
-         int len = exact_log2 (mask + 1);
-         rtx op = XEXP (x, 0);
-
-         /* If this is masking some low-order bits, we may be able to
-            impose a stricter constraint on what bits of the operand are
-            required.  */
-
-         op = force_to_mode (op, mode, len > 0 ? MIN (len, bits) : bits,
-                             reg);
-
-         if (bits < HOST_BITS_PER_WIDE_INT)
-           mask &= ((HOST_WIDE_INT) 1 << bits) - 1;
-
-         /* 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);
+         x = simplify_and_const_int (x, op_mode, XEXP (x, 0),
+                                     mask & INTVAL (XEXP (x, 1)));
 
          /* 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
-            can't be wider), we don't need it.  */
+            is just some low-order bits.  If so, and it is MASK, we don't
+            need it.  */
 
          if (GET_CODE (x) == AND && GET_CODE (XEXP (x, 1)) == CONST_INT
-             && bits < HOST_BITS_PER_WIDE_INT
-             && INTVAL (XEXP (x, 1)) == ((HOST_WIDE_INT) 1 << bits) - 1)
+             && INTVAL (XEXP (x, 1)) == mask)
            x = XEXP (x, 0);
 
+         /* If it remains an AND, try making another AND with the bits
+            in the mode mask that aren't in MASK turned on.  If the
+            constant in the AND is wide enough, this might make a
+            cheaper constant.  */
+
+         if (GET_CODE (x) == AND && GET_CODE (XEXP (x, 1)) == CONST_INT
+             && GET_MODE_MASK (GET_MODE (x)) != mask)
+           {
+             HOST_WIDE_INT cval = (INTVAL (XEXP (x, 1))
+                                   | (GET_MODE_MASK (GET_MODE (x)) & ~ mask));
+             int width = GET_MODE_BITSIZE (GET_MODE (x));
+             rtx y;
+
+             /* If MODE is narrower that HOST_WIDE_INT and CVAL is a negative
+                number, sign extend it.  */
+             if (width > 0 && width < HOST_BITS_PER_WIDE_INT
+                 && (cval & ((HOST_WIDE_INT) 1 << (width - 1))) != 0)
+               cval |= (HOST_WIDE_INT) -1 << width;
+
+             y = gen_binary (AND, GET_MODE (x), XEXP (x, 0), GEN_INT (cval));
+             if (rtx_cost (y, SET) < rtx_cost (x, SET))
+               x = y;
+           }
+
          break;
        }
 
-      /* ... fall through ... */
+      goto binop;
 
     case PLUS:
+      /* In (and (plus FOO C1) M), if M is a mask that just turns off
+        low-order bits (as in an alignment operation) and FOO is already
+        aligned to that boundary, mask C1 to that boundary as well.
+        This may eliminate that PLUS and, later, the AND.  */
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+         && exact_log2 (- mask) >= 0
+         && (nonzero_bits (XEXP (x, 0), mode) & ~ mask) == 0
+         && (INTVAL (XEXP (x, 1)) & ~ mask) != 0)
+       return force_to_mode (plus_constant (XEXP (x, 0),
+                                            INTVAL (XEXP (x, 1)) & mask),
+                             mode, mask, reg, next_select);
+
+      /* ... fall through ... */
+
     case MINUS:
     case MULT:
+      /* For PLUS, MINUS and MULT, we need any bits less significant than the
+        most significant bit in MASK since carries from those bits will
+        affect the bits we are interested in.  */
+      mask = fuller_mask;
+      goto binop;
+
     case IOR:
     case XOR:
+      /* If X is (ior (lshiftrt FOO C1) C2), try to commute the IOR and
+        LSHIFTRT so we end up with an (and (lshiftrt (ior ...) ...) ...)
+        operation which may be a bitfield extraction.  Ensure that the
+        constant we form is not wider than the mode of X.  */
+
+      if (GET_CODE (XEXP (x, 0)) == LSHIFTRT
+         && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+         && INTVAL (XEXP (XEXP (x, 0), 1)) >= 0
+         && INTVAL (XEXP (XEXP (x, 0), 1)) < HOST_BITS_PER_WIDE_INT
+         && GET_CODE (XEXP (x, 1)) == CONST_INT
+         && ((INTVAL (XEXP (XEXP (x, 0), 1))
+              + floor_log2 (INTVAL (XEXP (x, 1))))
+             < GET_MODE_BITSIZE (GET_MODE (x)))
+         && (INTVAL (XEXP (x, 1))
+             & ~ nonzero_bits (XEXP (x, 0), GET_MODE (x)) == 0))
+       {
+         temp = GEN_INT ((INTVAL (XEXP (x, 1)) & mask)
+                             << INTVAL (XEXP (XEXP (x, 0), 1)));
+         temp = gen_binary (GET_CODE (x), GET_MODE (x),
+                            XEXP (XEXP (x, 0), 0), temp);
+         x = gen_binary (LSHIFTRT, GET_MODE (x), temp, XEXP (x, 1));
+         return force_to_mode (x, mode, mask, reg, next_select);
+       }
+
+    binop:
       /* For most binary operations, just propagate into the operation and
-        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)));
+        change the mode if we have an operation of that mode.   */
+
+      op0 = gen_lowpart_for_combine (op_mode,
+                                    force_to_mode (XEXP (x, 0), mode, mask,
+                                                   reg, next_select));
+      op1 = gen_lowpart_for_combine (op_mode,
+                                    force_to_mode (XEXP (x, 1), mode, mask,
+                                                   reg, next_select));
+
+      /* If OP1 is a CONST_INT and X is an IOR or XOR, clear bits outside
+        MASK since OP1 might have been sign-extended but we never want
+        to turn on extra bits, since combine might have previously relied
+        on them being off.  */
+      if (GET_CODE (op1) == CONST_INT && (code == IOR || code == XOR)
+         && (INTVAL (op1) & mask) != 0)
+       op1 = GEN_INT (INTVAL (op1) & mask);
+        
+      if (op_mode != GET_MODE (x) || op0 != XEXP (x, 0) || op1 != XEXP (x, 1))
+       x = gen_binary (code, op_mode, op0, op1);
       break;
 
     case ASHIFT:
-    case LSHIFT:
       /* For left shifts, do the same, but just for 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.  */
+        wider mode.  */
 
       if (! (GET_CODE (XEXP (x, 1)) == CONST_INT
+            && INTVAL (XEXP (x, 1)) >= 0
             && 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) GET_MODE_BITSIZE (mode))))
+                   < (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));
-
-      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));
+      /* If the shift count is a constant and we can do arithmetic in
+        the mode of the shift, refine which bits we need.  Otherwise, use the
+        conservative form of the mask.  */
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+         && INTVAL (XEXP (x, 1)) >= 0
+         && INTVAL (XEXP (x, 1)) < GET_MODE_BITSIZE (op_mode)
+         && GET_MODE_BITSIZE (op_mode) <= HOST_BITS_PER_WIDE_INT)
+       mask >>= INTVAL (XEXP (x, 1));
+      else
+       mask = fuller_mask;
+
+      op0 = gen_lowpart_for_combine (op_mode,
+                                    force_to_mode (XEXP (x, 0), op_mode,
+                                                   mask, reg, next_select));
+
+      if (op_mode != GET_MODE (x) || op0 != XEXP (x, 0))
+       x =  gen_binary (code, op_mode, op0, 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.  In that
-        case, we can do the shift in MODE.  */
+      /* Here we can only do something if the shift count is a constant,
+        this shift constant is valid for the host, and we can do arithmetic
+        in OP_MODE.  */
 
       if (GET_CODE (XEXP (x, 1)) == CONST_INT
-         && INTVAL (XEXP (x, 1)) + bits <= GET_MODE_BITSIZE (mode))
+         && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT
+         && GET_MODE_BITSIZE (op_mode) <= HOST_BITS_PER_WIDE_INT)
        {
-         rtx inner = force_to_mode (XEXP (x, 0), mode,
-                                    bits + INTVAL (XEXP (x, 1)), reg);
+         rtx inner = XEXP (x, 0);
 
-         if (lshr_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+         /* Select the mask of the bits we need for the shift operand.  */
+         mask <<= INTVAL (XEXP (x, 1));
+
+         /* We can only change the mode of the shift if we can do arithmetic
+            in the mode of the shift and MASK is no wider than the width of
+            OP_MODE.  */
+         if (GET_MODE_BITSIZE (op_mode) > HOST_BITS_PER_WIDE_INT
+             || (mask & ~ GET_MODE_MASK (op_mode)) != 0)
            op_mode = GET_MODE (x);
 
-         x = gen_binary (LSHIFTRT, op_mode,
-                         gen_lowpart_for_combine (op_mode, inner),
-                         XEXP (x, 1));
+         inner = force_to_mode (inner, op_mode, mask, reg, next_select);
+
+         if (GET_MODE (x) != op_mode || inner != XEXP (x, 0))
+           x = gen_binary (LSHIFTRT, op_mode, inner, XEXP (x, 1));
+       }
+
+      /* 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 (x) == LSHIFTRT
+         && GET_CODE (XEXP (x, 1)) == CONST_INT
+         && ((INTVAL (XEXP (x, 1))
+              + num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0))))
+             >= GET_MODE_BITSIZE (GET_MODE (x)))
+         && exact_log2 (mask + 1) >= 0
+         && (num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0)))
+             >= exact_log2 (mask + 1)))
+       x = gen_binary (LSHIFTRT, GET_MODE (x), XEXP (x, 0),
+                       GEN_INT (GET_MODE_BITSIZE (GET_MODE (x))
+                                - exact_log2 (mask + 1)));
+      break;
+
+    case ASHIFTRT:
+      /* If we are just looking for the sign bit, we don't need this shift at
+        all, even if it has a variable count.  */
+      if (mask == ((HOST_WIDE_INT) 1
+                  << (GET_MODE_BITSIZE (GET_MODE (x)) - 1)))
+       return force_to_mode (XEXP (x, 0), mode, mask, reg, next_select);
+
+      /* If this is a shift by a constant, get a mask that contains those bits
+        that are not copies of the sign bit.  We then have two cases:  If
+        MASK only includes those bits, this can be a logical shift, which may
+        allow simplifications.  If MASK is a single-bit field not within
+        those bits, we are requesting a copy of the sign bit and hence can
+        shift the sign bit to the appropriate location.  */
+
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) >= 0
+         && INTVAL (XEXP (x, 1)) < HOST_BITS_PER_WIDE_INT)
+       {
+         int i = -1;
+
+         nonzero = GET_MODE_MASK (GET_MODE (x));
+         nonzero >>= INTVAL (XEXP (x, 1));
+
+         if ((mask & ~ nonzero) == 0
+             || (i = exact_log2 (mask)) >= 0)
+           {
+             x = simplify_shift_const
+               (x, LSHIFTRT, GET_MODE (x), XEXP (x, 0),
+                i < 0 ? INTVAL (XEXP (x, 1))
+                : GET_MODE_BITSIZE (GET_MODE (x)) - 1 - i);
+
+             if (GET_CODE (x) != ASHIFTRT)
+               return force_to_mode (x, mode, mask, reg, next_select);
+           }
+       }
+
+      /* If MASK is 1, convert this to a LSHIFTRT.  This can be done
+        even if the shift count isn't a constant.  */
+      if (mask == 1)
+       x = gen_binary (LSHIFTRT, GET_MODE (x), XEXP (x, 0), XEXP (x, 1));
+
+      /* If this is a sign-extension operation that just affects bits
+        we don't care about, remove it.  Be sure the call above returned
+        something that is still a shift.  */
+
+      if ((GET_CODE (x) == LSHIFTRT || GET_CODE (x) == ASHIFTRT)
+         && GET_CODE (XEXP (x, 1)) == CONST_INT
+         && INTVAL (XEXP (x, 1)) >= 0
+         && (INTVAL (XEXP (x, 1))
+             <= GET_MODE_BITSIZE (GET_MODE (x)) - (floor_log2 (mask) + 1))
+         && 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, mask,
+                             reg, next_select);
+
+      break;
+
+    case ROTATE:
+    case ROTATERT:
+      /* If the shift count is constant and we can do computations
+        in the mode of X, compute where the bits we care about are.
+        Otherwise, we can't do anything.  Don't change the mode of
+        the shift or propagate MODE into the shift, though.  */
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+         && INTVAL (XEXP (x, 1)) >= 0)
+       {
+         temp = simplify_binary_operation (code == ROTATE ? ROTATERT : ROTATE,
+                                           GET_MODE (x), GEN_INT (mask),
+                                           XEXP (x, 1));
+         if (temp && GET_CODE(temp) == CONST_INT)
+           SUBST (XEXP (x, 0),
+                  force_to_mode (XEXP (x, 0), GET_MODE (x),
+                                 INTVAL (temp), reg, next_select));
+       }
+      break;
+       
+    case NEG:
+      /* We need any bits less significant than the most significant bit in
+        MASK since carries from those bits will affect the bits we are
+        interested in.  */
+      mask = fuller_mask;
+      goto unop;
+
+    case NOT:
+      /* (not FOO) is (xor FOO CONST), so if FOO is an LSHIFTRT, we can do the
+        same as the XOR case above.  Ensure that the constant we form is not
+        wider than the mode of X.  */
+
+      if (GET_CODE (XEXP (x, 0)) == LSHIFTRT
+         && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+         && INTVAL (XEXP (XEXP (x, 0), 1)) >= 0
+         && (INTVAL (XEXP (XEXP (x, 0), 1)) + floor_log2 (mask)
+             < GET_MODE_BITSIZE (GET_MODE (x)))
+         && INTVAL (XEXP (XEXP (x, 0), 1)) < HOST_BITS_PER_WIDE_INT)
+       {
+         temp = GEN_INT (mask << INTVAL (XEXP (XEXP (x, 0), 1)));
+         temp = gen_binary (XOR, GET_MODE (x), XEXP (XEXP (x, 0), 0), temp);
+         x = gen_binary (LSHIFTRT, GET_MODE (x), temp, XEXP (XEXP (x, 0), 1));
+
+         return force_to_mode (x, mode, mask, reg, next_select);
        }
+
+    unop:
+      op0 = gen_lowpart_for_combine (op_mode,
+                                    force_to_mode (XEXP (x, 0), mode, mask,
+                                                   reg, next_select));
+      if (op_mode != GET_MODE (x) || op0 != XEXP (x, 0))
+       x = gen_unary (code, op_mode, op0);
+      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 bits that might be nonzero not
+        in CONST.  */
+      if ((mask & ~ STORE_FLAG_VALUE) == 0 && XEXP (x, 0) == const0_rtx
+         && (nonzero_bits (XEXP (x, 0), mode) & ~ mask) == 0)
+       return force_to_mode (XEXP (x, 0), mode, mask, reg, next_select);
+
+      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,
+                                                    mask, reg, next_select)));
+      SUBST (XEXP (x, 2),
+            gen_lowpart_for_combine (GET_MODE (x),
+                                     force_to_mode (XEXP (x, 2), mode,
+                                                    mask, reg,next_select)));
       break;
+    }
+
+  /* Ensure we return a value of the proper mode.  */
+  return gen_lowpart_for_combine (mode, x);
+}
+\f
+/* Return nonzero if X is an expression that has one of two values depending on
+   whether some other value is zero or nonzero.  In that case, we return the
+   value that is being tested, *PTRUE is set to the value if the rtx being
+   returned has a nonzero value, and *PFALSE is set to the other alternative.
+
+   If we return zero, we set *PTRUE and *PFALSE to X.  */
+
+static rtx
+if_then_else_cond (x, ptrue, pfalse)
+     rtx x;
+     rtx *ptrue, *pfalse;
+{
+  enum machine_mode mode = GET_MODE (x);
+  enum rtx_code code = GET_CODE (x);
+  int size = GET_MODE_BITSIZE (mode);
+  rtx cond0, cond1, true0, true1, false0, false1;
+  unsigned HOST_WIDE_INT nz;
+
+  /* If this is a unary operation whose operand has one of two values, apply
+     our opcode to compute those values.  */
+  if (GET_RTX_CLASS (code) == '1'
+      && (cond0 = if_then_else_cond (XEXP (x, 0), &true0, &false0)) != 0)
+    {
+      *ptrue = gen_unary (code, mode, true0);
+      *pfalse = gen_unary (code, mode, false0);
+      return cond0;
+    }
+
+  /* If this is a COMPARE, do nothing, since the IF_THEN_ELSE we would
+     make can't possibly match and would supress other optimizations.  */
+  else if (code == COMPARE)
+    ;
+
+  /* If this is a binary operation, see if either side has only one of two
+     values.  If either one does or if both do and they are conditional on
+     the same value, compute the new true and false values.  */
+  else if (GET_RTX_CLASS (code) == 'c' || GET_RTX_CLASS (code) == '2'
+          || GET_RTX_CLASS (code) == '<')
+    {
+      cond0 = if_then_else_cond (XEXP (x, 0), &true0, &false0);
+      cond1 = if_then_else_cond (XEXP (x, 1), &true1, &false1);
+
+      if ((cond0 != 0 || cond1 != 0)
+         && ! (cond0 != 0 && cond1 != 0 && ! rtx_equal_p (cond0, cond1)))
+       {
+         *ptrue = gen_binary (code, mode, true0, true1);
+         *pfalse = gen_binary (code, mode, false0, false1);
+         return cond0 ? cond0 : cond1;
+       }
+
+#if STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1
+
+      /* See if we have PLUS, IOR, XOR, MINUS or UMAX, where one of the
+        operands is zero when the other is non-zero, and vice-versa.  */
+
+      if ((code == PLUS || code == IOR || code == XOR || code == MINUS
+          || code == UMAX)
+         && GET_CODE (XEXP (x, 0)) == MULT && GET_CODE (XEXP (x, 1)) == MULT)
+       {
+         rtx op0 = XEXP (XEXP (x, 0), 1);
+         rtx op1 = XEXP (XEXP (x, 1), 1);
+
+         cond0 = XEXP (XEXP (x, 0), 0);
+         cond1 = XEXP (XEXP (x, 1), 0);
+
+         if (GET_RTX_CLASS (GET_CODE (cond0)) == '<'
+             && GET_RTX_CLASS (GET_CODE (cond1)) == '<'
+             && reversible_comparison_p (cond1)
+             && ((GET_CODE (cond0) == reverse_condition (GET_CODE (cond1))
+                  && rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 0))
+                  && rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 1)))
+                 || ((swap_condition (GET_CODE (cond0))
+                      == reverse_condition (GET_CODE (cond1)))
+                     && rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 1))
+                     && rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 0))))
+             && ! side_effects_p (x))
+           {
+             *ptrue = gen_binary (MULT, mode, op0, const_true_rtx);
+             *pfalse = gen_binary (MULT, mode, 
+                                   (code == MINUS 
+                                    ? gen_unary (NEG, mode, op1) : op1),
+                                   const_true_rtx);
+             return cond0;
+           }
+       }
+
+      /* Similarly for MULT, AND and UMIN, execpt that for these the result
+        is always zero.  */
+      if ((code == MULT || code == AND || code == UMIN)
+         && GET_CODE (XEXP (x, 0)) == MULT && GET_CODE (XEXP (x, 1)) == MULT)
+       {
+         cond0 = XEXP (XEXP (x, 0), 0);
+         cond1 = XEXP (XEXP (x, 1), 0);
+
+         if (GET_RTX_CLASS (GET_CODE (cond0)) == '<'
+             && GET_RTX_CLASS (GET_CODE (cond1)) == '<'
+             && reversible_comparison_p (cond1)
+             && ((GET_CODE (cond0) == reverse_condition (GET_CODE (cond1))
+                  && rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 0))
+                  && rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 1)))
+                 || ((swap_condition (GET_CODE (cond0))
+                      == reverse_condition (GET_CODE (cond1)))
+                     && rtx_equal_p (XEXP (cond0, 0), XEXP (cond1, 1))
+                     && rtx_equal_p (XEXP (cond0, 1), XEXP (cond1, 0))))
+             && ! side_effects_p (x))
+           {
+             *ptrue = *pfalse = const0_rtx;
+             return cond0;
+           }
+       }
+#endif
+    }
+
+  else if (code == IF_THEN_ELSE)
+    {
+      /* If we have IF_THEN_ELSE already, extract the condition and
+        canonicalize it if it is NE or EQ.  */
+      cond0 = XEXP (x, 0);
+      *ptrue = XEXP (x, 1), *pfalse = XEXP (x, 2);
+      if (GET_CODE (cond0) == NE && XEXP (cond0, 1) == const0_rtx)
+       return XEXP (cond0, 0);
+      else if (GET_CODE (cond0) == EQ && XEXP (cond0, 1) == const0_rtx)
+       {
+         *ptrue = XEXP (x, 2), *pfalse = XEXP (x, 1);
+         return XEXP (cond0, 0);
+       }
+      else
+       return cond0;
+    }
 
-    case ASHIFTRT:
-      /* If this is a sign-extension operation that just affects bits
-        we don't care about, remove it.  */
+  /* If X is a normal SUBREG with both inner and outer modes integral,
+     we can narrow both the true and false values of the inner expression,
+     if there is a condition.  */
+  else if (code == SUBREG && GET_MODE_CLASS (mode) == MODE_INT
+          && GET_MODE_CLASS (GET_MODE (SUBREG_REG (x))) == MODE_INT
+          && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
+          && 0 != (cond0 = if_then_else_cond (SUBREG_REG (x),
+                                              &true0, &false0)))
+    {
+      *ptrue = force_to_mode (true0, mode, GET_MODE_MASK (mode), NULL_RTX, 0);
+      *pfalse
+       = force_to_mode (false0, mode, GET_MODE_MASK (mode), NULL_RTX, 0);
 
-      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;
+      return cond0;
+    }
 
-    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. */
-      x = gen_unary (code, op_mode,
-                    gen_lowpart_for_combine (op_mode,
-                                             force_to_mode (XEXP (x, 0), mode,
-                                                            bits, reg)));
-      break;
+  /* If X is a constant, this isn't special and will cause confusions
+     if we treat it as such.  Likewise if it is equivalent to a constant.  */
+  else if (CONSTANT_P (x)
+          || ((cond0 = get_last_value (x)) != 0 && CONSTANT_P (cond0)))
+    ;
 
-    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.  */
+  /* If X is known to be either 0 or -1, those are the true and 
+     false values when testing X.  */
+  else if (num_sign_bit_copies (x, mode) == size)
+    {
+      *ptrue = constm1_rtx, *pfalse = const0_rtx;
+      return x;
+    }
 
-      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;
+  /* Likewise for 0 or a single bit.  */
+  else if (exact_log2 (nz = nonzero_bits (x, mode)) >= 0)
+    {
+      *ptrue = GEN_INT (nz), *pfalse = const0_rtx;
+      return x;
     }
 
-  /* Ensure we return a value of the proper mode.  */
-  return gen_lowpart_for_combine (mode, x);
+  /* Otherwise fail; show no condition with true and false values the same.  */
+  *ptrue = *pfalse = x;
+  return 0;
 }
 \f
 /* Return the value of expression X given the fact that condition COND
@@ -5500,7 +6258,7 @@ known_cond (x, cond, reg, val)
      rtx reg, val;
 {
   enum rtx_code code = GET_CODE (x);
-  rtx new, temp;
+  rtx temp;
   char *fmt;
   int i, j;
 
@@ -5586,7 +6344,6 @@ make_field_assignment (x)
 {
   rtx dest = SET_DEST (x);
   rtx src = SET_SRC (x);
-  rtx ourdest;
   rtx assign;
   HOST_WIDE_INT c1;
   int pos, len;
@@ -5663,7 +6420,7 @@ make_field_assignment (x)
   else
     return x;
 
-  pos = get_pos_from_mask (~c1, &len);
+  pos = get_pos_from_mask (c1 ^ GET_MODE_MASK (GET_MODE (dest)), &len);
   if (pos < 0 || pos + len > GET_MODE_BITSIZE (GET_MODE (dest))
       || (GET_MODE_BITSIZE (GET_MODE (other)) <= HOST_BITS_PER_WIDE_INT
          && (c1 & nonzero_bits (other, GET_MODE (other))) != 0))
@@ -5681,7 +6438,11 @@ make_field_assignment (x)
 
   src = force_to_mode (simplify_shift_const (NULL_RTX, LSHIFTRT,
                                             GET_MODE (src), other, pos),
-                      mode, len, dest);
+                      mode,
+                      GET_MODE_BITSIZE (mode) >= HOST_BITS_PER_WIDE_INT
+                      ? GET_MODE_MASK (mode)
+                      : ((HOST_WIDE_INT) 1 << len) - 1,
+                      dest, 0);
 
   return gen_rtx_combine (SET, VOIDmode, assign, src);
 }
@@ -5701,7 +6462,7 @@ apply_distributive_law (x)
   /* Distributivity is not true for floating point.
      It can change the value.  So don't do it.
      -- rms and moshier@world.std.com.  */
-  if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+  if (FLOAT_MODE_P (GET_MODE (x)))
     return x;
 
   /* The outer operation can only be one of the following:  */
@@ -5740,8 +6501,7 @@ apply_distributive_law (x)
       break;
 
     case ASHIFT:
-    case LSHIFT:
-      /* These are also multiplies, so they distribute over everything.  */
+      /* This is also a multiply, so it distributes over everything.  */
       break;
 
     case SUBREG:
@@ -5823,269 +6583,17 @@ simplify_and_const_int (x, mode, varop, constop)
      rtx varop;
      unsigned HOST_WIDE_INT constop;
 {
-  register enum machine_mode tmode;
-  register rtx temp;
   unsigned HOST_WIDE_INT nonzero;
+  int i;
 
-  /* There is a large class of optimizations based on the principle that
-     some operations produce results where certain bits are known to be zero,
-     and hence are not significant to the AND.  For example, if we have just
-     done a left shift of one bit, the low-order bit is known to be zero and
-     hence an AND with a mask of ~1 would not do anything.
-
-     At the end of the following loop, we set:
-
-     VAROP to be the item to be AND'ed with;
-     CONSTOP to the constant value to AND it with.  */
-
-  while (1)
-    {
-      /* If we ever encounter a mode wider than the host machine's widest
-        integer size, we can't compute the masks accurately, so give up.  */
-      if (GET_MODE_BITSIZE (GET_MODE (varop)) > HOST_BITS_PER_WIDE_INT)
-       break;
-
-      /* Unless one of the cases below does a `continue',
-        a `break' will be executed to exit the loop.  */
-
-      switch (GET_CODE (varop))
-       {
-       case CLOBBER:
-         /* If VAROP is a (clobber (const_int)), return it since we know
-            we are generating something that won't match. */
-         return varop;
-
-#if ! BITS_BIG_ENDIAN
-       case USE:
-         /* VAROP is a (use (mem ..)) that was made from a bit-field
-            extraction that spanned the boundary of the MEM.  If we are
-            now masking so it is within that boundary, we don't need the
-            USE any more.  */
-         if ((constop & ~ GET_MODE_MASK (GET_MODE (XEXP (varop, 0)))) == 0)
-           {
-             varop = XEXP (varop, 0);
-             continue;
-           }
-         break;
-#endif
-
-       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 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_EXTEND
-                 || (0 == (constop
-                           & GET_MODE_MASK (GET_MODE (varop))
-                           & ~ GET_MODE_MASK (GET_MODE (SUBREG_REG (varop)))))
-#endif
-                 ))
-           {
-             varop = SUBREG_REG (varop);
-             continue;
-           }
-         break;
-
-       case ZERO_EXTRACT:
-       case SIGN_EXTRACT:
-       case ZERO_EXTEND:
-       case SIGN_EXTEND:
-         /* Try to expand these into a series of shifts and then work
-            with that result.  If we can't, for example, if the extract
-            isn't at a fixed position, give up.  */
-         temp = expand_compound_operation (varop);
-         if (temp != varop)
-           {
-             varop = temp;
-             continue;
-           }
-         break;
-
-       case AND:
-         if (GET_CODE (XEXP (varop, 1)) == CONST_INT)
-           {
-             constop &= INTVAL (XEXP (varop, 1));
-             varop = XEXP (varop, 0);
-             continue;
-           }
-         break;
-
-       case IOR:
-       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.  */
-
-         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 (varop, 1))
-                 & ~ nonzero_bits (XEXP (varop, 0), GET_MODE (varop)) == 0))
-           {
-             temp = GEN_INT ((INTVAL (XEXP (varop, 1)) & constop)
-                             << INTVAL (XEXP (XEXP (varop, 0), 1)));
-             temp = gen_binary (GET_CODE (varop), GET_MODE (varop),
-                                XEXP (XEXP (varop, 0), 0), temp);
-             varop = gen_rtx_combine (LSHIFTRT, GET_MODE (varop),
-                                      temp, XEXP (varop, 1));
-             continue;
-           }
-
-         /* Apply the AND to both branches of the IOR or XOR, then try to
-            apply the distributive law.  This may eliminate operations 
-            if either branch can be simplified because of the AND.
-            It may also make some cases more complex, but those cases
-            probably won't match a pattern either with or without this.  */
-         return 
-           gen_lowpart_for_combine
-             (mode, apply_distributive_law
-              (gen_rtx_combine
-               (GET_CODE (varop), GET_MODE (varop),
-                simplify_and_const_int (NULL_RTX, GET_MODE (varop),
-                                        XEXP (varop, 0), constop),
-                simplify_and_const_int (NULL_RTX, GET_MODE (varop),
-                                        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.  */
-
-         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)
-           {
-             temp = GEN_INT (constop << INTVAL (XEXP (XEXP (varop, 0), 1)));
-             temp = gen_binary (XOR, GET_MODE (varop),
-                                XEXP (XEXP (varop, 0), 0), temp);
-             varop = gen_rtx_combine (LSHIFTRT, GET_MODE (varop),
-                                      temp, XEXP (XEXP (varop, 0), 1));
-             continue;
-           }
-         break;
-
-       case ASHIFTRT:
-         /* If we are just looking for the sign bit, we don't need this
-            shift at all, even if it has a variable count.  */
-         if (constop == ((HOST_WIDE_INT) 1
-                         << (GET_MODE_BITSIZE (GET_MODE (varop)) - 1)))
-           {
-             varop = XEXP (varop, 0);
-             continue;
-           }
-
-         /* If this is a shift by a constant, get a mask that contains
-            those bits that are not copies of the sign bit.  We then have
-            two cases:  If CONSTOP only includes those bits, this can be
-            a logical shift, which may allow simplifications.  If CONSTOP
-            is a single-bit field not within those bits, we are requesting
-            a copy of the sign bit and hence can shift the sign bit to
-            the appropriate location.  */
-         if (GET_CODE (XEXP (varop, 1)) == CONST_INT
-             && INTVAL (XEXP (varop, 1)) >= 0
-             && INTVAL (XEXP (varop, 1)) < HOST_BITS_PER_WIDE_INT)
-           {
-             int i = -1;
-
-             nonzero = GET_MODE_MASK (GET_MODE (varop));
-             nonzero >>= INTVAL (XEXP (varop, 1));
-
-             if ((constop & ~ nonzero) == 0
-                 || (i = exact_log2 (constop)) >= 0)
-               {
-                 varop = simplify_shift_const
-                   (varop, LSHIFTRT, GET_MODE (varop), XEXP (varop, 0),
-                    i < 0 ? INTVAL (XEXP (varop, 1))
-                    : GET_MODE_BITSIZE (GET_MODE (varop)) - 1 - i);
-                 if (GET_CODE (varop) != ASHIFTRT)
-                   continue;
-               }
-           }
-
-         /* If our mask is 1, convert this to a LSHIFTRT.  This can be done
-            even if the shift count isn't a constant.  */
-         if (constop == 1)
-           varop = gen_rtx_combine (LSHIFTRT, GET_MODE (varop),
-                                    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)
-           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 bits that might be
-            nonzero not in CONST.  */
-         if ((constop & ~ STORE_FLAG_VALUE) == 0
-             && XEXP (varop, 0) == const0_rtx
-             && (nonzero_bits (XEXP (varop, 0), mode) & ~ constop) == 0)
-           {
-             varop = XEXP (varop, 0);
-             continue;
-           }
-         break;
-
-       case PLUS:
-         /* In (and (plus FOO C1) M), if M is a mask that just turns off
-            low-order bits (as in an alignment operation) and FOO is already
-            aligned to that boundary, we can convert remove this AND
-            and possibly the PLUS if it is now adding zero.  */
-         if (GET_CODE (XEXP (varop, 1)) == CONST_INT
-             && exact_log2 (-constop) >= 0
-             && (nonzero_bits (XEXP (varop, 0), mode) & ~ constop) == 0)
-           {
-             varop = plus_constant (XEXP (varop, 0),
-                                    INTVAL (XEXP (varop, 1)) & constop);
-             constop = ~0;
-             break;
-           }
-
-         /* ... fall through ... */
-
-       case MINUS:
-         /* In (and (plus (and FOO M1) BAR) M2), if M1 and M2 are one
-            less than powers of two and M2 is narrower than M1, we can
-            eliminate the inner AND.  This occurs when incrementing
-            bit fields.  */
-
-         if (GET_CODE (XEXP (varop, 0)) == ZERO_EXTRACT
-             || GET_CODE (XEXP (varop, 0)) == ZERO_EXTEND)
-           SUBST (XEXP (varop, 0),
-                  expand_compound_operation (XEXP (varop, 0)));
-
-         if (GET_CODE (XEXP (varop, 0)) == AND
-             && GET_CODE (XEXP (XEXP (varop, 0), 1)) == CONST_INT
-             && exact_log2 (constop + 1) >= 0
-             && exact_log2 (INTVAL (XEXP (XEXP (varop, 0), 1)) + 1) >= 0
-             && (~ INTVAL (XEXP (XEXP (varop, 0), 1)) & constop) == 0)
-           SUBST (XEXP (varop, 0), XEXP (XEXP (varop, 0), 0));
-         break;
-       }
-
-      break;
-    }
+  /* Simplify VAROP knowing that we will be only looking at some of the
+     bits in it.  */
+  varop = force_to_mode (varop, mode, constop, NULL_RTX, 0);
 
-  /* If we have reached a constant, this whole thing is constant.  */
-  if (GET_CODE (varop) == CONST_INT)
-    return GEN_INT (constop & INTVAL (varop));
+  /* If VAROP is a CLOBBER, we will fail so return it; if it is a
+     CONST_INT, we are done.  */
+  if (GET_CODE (varop) == CLOBBER || GET_CODE (varop) == CONST_INT)
+    return varop;
 
   /* 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
@@ -6103,6 +6611,29 @@ simplify_and_const_int (x, mode, varop, constop)
   if (constop == 0)
     return const0_rtx;
 
+  /* If VAROP is a NEG of something known to be zero or 1 and CONSTOP is
+     a power of two, we can replace this with a ASHIFT.  */
+  if (GET_CODE (varop) == NEG && nonzero_bits (XEXP (varop, 0), mode) == 1
+      && (i = exact_log2 (constop)) >= 0)
+    return simplify_shift_const (NULL_RTX, ASHIFT, mode, XEXP (varop, 0), i);
+                                
+  /* If VAROP is an IOR or XOR, apply the AND to both branches of the IOR
+     or XOR, then try to apply the distributive law.  This may eliminate
+     operations if either branch can be simplified because of the AND.
+     It may also make some cases more complex, but those cases probably
+     won't match a pattern either with or without this.  */
+
+  if (GET_CODE (varop) == IOR || GET_CODE (varop) == XOR)
+    return
+      gen_lowpart_for_combine
+       (mode,
+        apply_distributive_law
+        (gen_binary (GET_CODE (varop), GET_MODE (varop),
+                     simplify_and_const_int (NULL_RTX, GET_MODE (varop),
+                                             XEXP (varop, 0), constop),
+                     simplify_and_const_int (NULL_RTX, GET_MODE (varop),
+                                             XEXP (varop, 1), constop))));
+
   /* Get VAROP in MODE.  Try to get a SUBREG if not.  Don't make a new SUBREG
      if we already had one (just check for the simplest cases).  */
   if (x && GET_CODE (XEXP (x, 0)) == SUBREG
@@ -6122,7 +6653,7 @@ simplify_and_const_int (x, mode, varop, constop)
 
   /* Otherwise, return an AND.  See how much, if any, of X we can use.  */
   else if (x == 0 || GET_CODE (x) != AND || GET_MODE (x) != mode)
-    x = gen_rtx_combine (AND, mode, varop, GEN_INT (constop));
+    x = gen_binary (AND, mode, varop, GEN_INT (constop));
 
   else
     {
@@ -6166,6 +6697,25 @@ nonzero_bits (x, mode)
        just return the mode mask.  Those tests will then be false.  */
     return nonzero;
 
+#ifndef WORD_REGISTER_OPERATIONS
+  /* If MODE is wider than X, but both are a single word for both the host
+     and target machines, we can compute this from which bits of the 
+     object might be nonzero in its own mode, taking into account the fact
+     that 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 (x) != VOIDmode && GET_MODE (x) != mode
+      && GET_MODE_BITSIZE (GET_MODE (x)) <= BITS_PER_WORD
+      && GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT
+      && GET_MODE_BITSIZE (mode) > GET_MODE_BITSIZE (GET_MODE (x)))
+    {
+      nonzero &= nonzero_bits (x, GET_MODE (x));
+      nonzero |= GET_MODE_MASK (mode) & ~ GET_MODE_MASK (GET_MODE (x));
+      return nonzero;
+    }
+#endif
+
   code = GET_CODE (x);
   switch (code)
     {
@@ -6201,41 +6751,70 @@ nonzero_bits (x, mode)
        return reg_last_set_nonzero_bits[REGNO (x)];
 
       tem = get_last_value (x);
+
       if (tem)
-       return nonzero_bits (tem, mode);
+       {
+#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)) - 1))))
+           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 nonzero;
 
     case CONST_INT:
+#ifdef SHORT_IMMEDIATES_SIGN_EXTEND
+      /* If X is negative in MODE, sign-extend the value.  */
+      if (INTVAL (x) > 0 && mode_width < BITS_PER_WORD
+         && 0 != (INTVAL (x) & ((HOST_WIDE_INT) 1 << (mode_width - 1))))
+       return (INTVAL (x) | ((HOST_WIDE_INT) (-1) << mode_width));
+#endif
+
       return INTVAL (x);
 
-#ifdef BYTE_LOADS_ZERO_EXTEND
     case MEM:
+#ifdef LOAD_EXTEND_OP
       /* 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.  */
-      nonzero &= GET_MODE_MASK (GET_MODE (x));
-      break;
+      if (LOAD_EXTEND_OP (GET_MODE (x)) == ZERO_EXTEND)
+       nonzero &= GET_MODE_MASK (GET_MODE (x));
 #endif
+      break;
 
-#if STORE_FLAG_VALUE == 1
     case EQ:  case NE:
     case GT:  case GTU:
     case LT:  case LTU:
     case GE:  case GEU:
     case LE:  case LEU:
 
-      if (GET_MODE_CLASS (mode) == MODE_INT)
-       nonzero = 1;
+      /* If this produces an integer result, we know which bits are set.
+        Code here used to clear bits outside the mode of X, but that is
+        now done above.  */
 
-      /* 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)
-       nonzero |= (GET_MODE_MASK (mode) & ~ GET_MODE_MASK (GET_MODE (x)));
+      if (GET_MODE_CLASS (mode) == MODE_INT
+         && mode_width <= HOST_BITS_PER_WIDE_INT)
+       nonzero = STORE_FLAG_VALUE;
       break;
-#endif
 
     case NEG:
       if (num_sign_bit_copies (XEXP (x, 0), GET_MODE (x))
@@ -6373,7 +6952,8 @@ nonzero_bits (x, mode)
              <= HOST_BITS_PER_WIDE_INT))
        {
          nonzero &= nonzero_bits (SUBREG_REG (x), mode);
-#ifndef BYTE_LOADS_EXTEND
+
+#ifndef WORD_REGISTER_OPERATIONS
          /* 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.  */
@@ -6388,7 +6968,6 @@ nonzero_bits (x, mode)
     case ASHIFTRT:
     case LSHIFTRT:
     case ASHIFT:
-    case LSHIFT:
     case ROTATE:
       /* 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
@@ -6423,7 +7002,7 @@ nonzero_bits (x, mode)
              if (inner & ((HOST_WIDE_INT) 1 << (width - 1 - count)))
                inner |= (((HOST_WIDE_INT) 1 << count) - 1) << (width - count);
            }
-         else if (code == LSHIFT || code == ASHIFT)
+         else if (code == ASHIFT)
            inner <<= count;
          else
            inner = ((inner << (count % width)
@@ -6448,9 +7027,9 @@ nonzero_bits (x, mode)
 }
 \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.  */
+   be equal to the sign bit.  X will be used in mode MODE; if MODE is
+   VOIDmode, X will be used in its own mode.  The returned value  will always
+   be between 1 and the number of bits in MODE.  */
 
 static int
 num_sign_bit_copies (x, mode)
@@ -6474,6 +7053,19 @@ num_sign_bit_copies (x, mode)
 
   bitwidth = GET_MODE_BITSIZE (mode);
 
+  /* For a smaller object, just ignore the high bits. */
+  if (bitwidth < GET_MODE_BITSIZE (GET_MODE (x)))
+    return MAX (1, (num_sign_bit_copies (x, GET_MODE (x))
+                   - (GET_MODE_BITSIZE (GET_MODE (x)) - bitwidth)));
+     
+#ifndef WORD_REGISTER_OPERATIONS
+  /* If this machine does not do all register operations on the entire
+     register and MODE is wider than the mode of X, we can say nothing
+     at all about the high-order bits.  */
+  if (GET_MODE (x) != VOIDmode && bitwidth > GET_MODE_BITSIZE (GET_MODE (x)))
+    return 1;
+#endif
+
   switch (code)
     {
     case REG:
@@ -6493,11 +7085,13 @@ num_sign_bit_copies (x, mode)
        return reg_sign_bit_copies[REGNO (x)];
       break;
 
-#ifdef BYTE_LOADS_SIGN_EXTEND
     case MEM:
+#ifdef LOAD_EXTEND_OP
       /* Some RISC machines sign-extend all loads of smaller than a word.  */
-      return MAX (1, bitwidth - GET_MODE_BITSIZE (GET_MODE (x)) + 1);
+      if (LOAD_EXTEND_OP (GET_MODE (x)) == SIGN_EXTEND)
+       return MAX (1, bitwidth - GET_MODE_BITSIZE (GET_MODE (x)) + 1);
 #endif
+      break;
 
     case CONST_INT:
       /* If the constant is negative, take its 1's complement and remask.
@@ -6515,8 +7109,8 @@ num_sign_bit_copies (x, mode)
         high-order bits are known to be sign bit copies.  */
 
       if (SUBREG_PROMOTED_VAR_P (x) && ! SUBREG_PROMOTED_UNSIGNED_P (x))
-       return (GET_MODE_BITSIZE (mode) - GET_MODE_BITSIZE (GET_MODE (x))
-               + num_sign_bit_copies (SUBREG_REG (x), GET_MODE (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))))
@@ -6527,18 +7121,16 @@ num_sign_bit_copies (x, mode)
                             - 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.  */
+#ifdef WORD_REGISTER_OPERATIONS
+      /* For paradoxical SUBREGs on machines where all register operations
+        affect the entire register, just look inside.  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:
@@ -6678,7 +7270,6 @@ num_sign_bit_copies (x, mode)
       return num0;
 
     case ASHIFT:
-    case LSHIFT:
       /* Left shifts destroy copies.  */
       if (GET_CODE (XEXP (x, 1)) != CONST_INT
          || INTVAL (XEXP (x, 1)) < 0
@@ -6709,7 +7300,7 @@ num_sign_bit_copies (x, mode)
     return 1;
 
   nonzero = nonzero_bits (x, mode);
-  return (nonzero == GET_MODE_MASK (mode)
+  return (nonzero & ((HOST_WIDE_INT) 1 << (bitwidth - 1))
          ? 1 : bitwidth - floor_log2 (nonzero) - 1);
 }
 \f
@@ -6891,7 +7482,7 @@ simplify_shift_const (x, code, result_mode, varop, count)
     = (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
   /* We form (outer_op (code varop count) (outer_const)).  */
   enum rtx_code outer_op = NIL;
-  HOST_WIDE_INT outer_const;
+  HOST_WIDE_INT outer_const = 0;
   rtx const_rtx;
   int complement_p = 0;
   rtx new;
@@ -6926,10 +7517,6 @@ simplify_shift_const (x, code, result_mode, varop, count)
       if (code == ROTATERT)
        code = ROTATE, count = GET_MODE_BITSIZE (result_mode) - count;
 
-      /* Canonicalize LSHIFT to ASHIFT.  */
-      if (code == LSHIFT)
-       code = ASHIFT;
-
       /* We need to determine what mode we will do the shift in.  If the
         shift is a ASHIFTRT or ROTATE, we must always do it in the mode it
         was originally done in.  Otherwise, we can do it in MODE, the widest
@@ -7121,7 +7708,6 @@ simplify_shift_const (x, code, result_mode, varop, count)
 
        case LSHIFTRT:
        case ASHIFT:
-       case LSHIFT:
        case ROTATE:
          /* Here we have two nested shifts.  The result is usually the
             AND of a new shift with a mask.  We compute the result below.  */
@@ -7135,10 +7721,6 @@ simplify_shift_const (x, code, result_mode, varop, count)
              int first_count = INTVAL (XEXP (varop, 1));
              unsigned HOST_WIDE_INT mask;
              rtx mask_rtx;
-             rtx inner;
-
-             if (first_code == LSHIFT)
-               first_code = ASHIFT;
 
              /* We have one common special case.  We can't do any merging if
                 the inner code is an ASHIFTRT of a smaller mode.  However, if
@@ -7319,6 +7901,7 @@ simplify_shift_const (x, code, result_mode, varop, count)
              && (new = simplify_binary_operation (code, result_mode,
                                                   XEXP (varop, 1),
                                                   GEN_INT (count))) != 0
+             && GET_CODE(new) == CONST_INT
              && merge_outer_ops (&outer_op, &outer_const, GET_CODE (varop),
                                  INTVAL (new), result_mode, &complement_p))
            {
@@ -7330,12 +7913,12 @@ simplify_shift_const (x, code, result_mode, varop, count)
             logical expression, make a new logical expression, and apply
             the inverse distributive law.  */
          {
-           rtx lhs = simplify_shift_const (NULL_RTX, code, result_mode,
+           rtx lhs = simplify_shift_const (NULL_RTX, code, shift_mode,
                                            XEXP (varop, 0), count);
-           rtx rhs = simplify_shift_const (NULL_RTX, code, result_mode,
+           rtx rhs = simplify_shift_const (NULL_RTX, code, shift_mode,
                                            XEXP (varop, 1), count);
 
-           varop = gen_binary (GET_CODE (varop), result_mode, lhs, rhs);
+           varop = gen_binary (GET_CODE (varop), shift_mode, lhs, rhs);
            varop = apply_distributive_law (varop);
 
            count = 0;
@@ -7343,11 +7926,11 @@ simplify_shift_const (x, code, result_mode, varop, count)
          break;
 
        case EQ:
-         /* convert (lshift (eq FOO 0) C) to (xor FOO 1) if STORE_FLAG_VALUE
+         /* convert (lshiftrt (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
-            may be nonzero.  */
-         if (code == LSHIFT
+            GET_MODE_BITSIZE (MODE) - 1, and FOO has only its low-order bit
+            that may be nonzero.  */
+         if (code == LSHIFTRT
              && XEXP (varop, 1) == const0_rtx
              && GET_MODE (XEXP (varop, 0)) == result_mode
              && count == GET_MODE_BITSIZE (result_mode) - 1
@@ -7438,6 +8021,7 @@ simplify_shift_const (x, code, result_mode, varop, count)
              && (new = simplify_binary_operation (ASHIFT, result_mode,
                                                   XEXP (varop, 1),
                                                   GEN_INT (count))) != 0
+             && GET_CODE(new) == CONST_INT
              && merge_outer_ops (&outer_op, &outer_const, PLUS,
                                  INTVAL (new), result_mode, &complement_p))
            {
@@ -7523,6 +8107,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)
@@ -7582,6 +8176,15 @@ recog_for_combine (pnewpat, insn, pnotes)
   int i;
   rtx notes = 0;
 
+  /* If PAT is a PARALLEL, check to see if it contains the CLOBBER
+     we use to indicate that something didn't match.  If we find such a
+     thing, force rejection.  */
+  if (GET_CODE (pat) == PARALLEL)
+    for (i = XVECLEN (pat, 0) - 1; i >= 0; i--)
+      if (GET_CODE (XVECEXP (pat, 0, i)) == CLOBBER
+         && XEXP (XVECEXP (pat, 0, i), 0) == const0_rtx)
+       return -1;
+
   /* Is the result of combination a valid instruction?  */
   insn_code_number = recog (pat, insn, &num_clobbers_to_add);
 
@@ -7753,21 +8356,26 @@ gen_lowpart_for_combine (mode, x)
 
 /*VARARGS2*/
 static rtx
-gen_rtx_combine (va_alist)
-     va_dcl
+gen_rtx_combine VPROTO((enum rtx_code code, enum machine_mode mode, ...))
 {
-  va_list p;
+#ifndef __STDC__
   enum rtx_code code;
   enum machine_mode mode;
+#endif
+  va_list p;
   int n_args;
   rtx args[3];
   int i, j;
   char *fmt;
   rtx rt;
 
-  va_start (p);
+  VA_START (p, mode);
+
+#ifndef __STDC__
   code = va_arg (p, enum rtx_code);
   mode = va_arg (p, enum machine_mode);
+#endif
+
   n_args = GET_RTX_LENGTH (code);
   fmt = GET_RTX_FORMAT (code);
 
@@ -7788,15 +8396,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.rtx) == code
-       && GET_MODE (undobuf.undo[i].old_contents.rtx) == mode)
+       && GET_CODE (undobuf.undo[i].old_contents.r) == code
+       && GET_MODE (undobuf.undo[i].old_contents.r) == mode)
       {
        for (j = 0; j < n_args; j++)
-         if (XEXP (undobuf.undo[i].old_contents.rtx, j) != args[j])
+         if (XEXP (undobuf.undo[i].old_contents.r, j) != args[j])
            break;
 
        if (j == n_args)
-         return undobuf.undo[i].old_contents.rtx;
+         return undobuf.undo[i].old_contents.r;
       }
 
   /* Otherwise make a new rtx.  We know we have 1, 2, or 3 args.
@@ -7833,6 +8441,16 @@ gen_binary (code, mode, op0, op1)
   if (GET_RTX_CLASS (code) == '<') 
     {
       enum machine_mode op_mode = GET_MODE (op0);
+
+      /* Strip the COMPARE from (REL_OP (compare X Y) 0) to get 
+        just (REL_OP X Y). */
+      if (GET_CODE (op0) == COMPARE && op1 == const0_rtx)
+       {
+         op1 = XEXP (op0, 1);
+         op0 = XEXP (op0, 0);
+         op_mode = GET_MODE (op0);
+       }
+
       if (op_mode == VOIDmode)
        op_mode = GET_MODE (op1);
       result = simplify_relational_operation (code, op_mode, op0, op1);
@@ -7896,6 +8514,34 @@ simplify_comparison (code, pop0, pop1)
   /* Try a few ways of applying the same transformation to both operands.  */
   while (1)
     {
+#ifndef WORD_REGISTER_OPERATIONS
+      /* The test below this one won't handle SIGN_EXTENDs on these machines,
+        so check specially.  */
+      if (code != GTU && code != GEU && code != LTU && code != LEU
+         && GET_CODE (op0) == ASHIFTRT && GET_CODE (op1) == ASHIFTRT
+         && GET_CODE (XEXP (op0, 0)) == ASHIFT
+         && GET_CODE (XEXP (op1, 0)) == ASHIFT
+         && GET_CODE (XEXP (XEXP (op0, 0), 0)) == SUBREG
+         && GET_CODE (XEXP (XEXP (op1, 0), 0)) == SUBREG
+         && (GET_MODE (SUBREG_REG (XEXP (XEXP (op0, 0), 0)))
+             == GET_MODE (SUBREG_REG (XEXP (XEXP (op1, 0), 0))))
+         && GET_CODE (XEXP (op0, 1)) == CONST_INT
+         && GET_CODE (XEXP (op1, 1)) == CONST_INT
+         && GET_CODE (XEXP (XEXP (op0, 0), 1)) == CONST_INT
+         && GET_CODE (XEXP (XEXP (op1, 0), 1)) == CONST_INT
+         && INTVAL (XEXP (op0, 1)) == INTVAL (XEXP (op1, 1))
+         && INTVAL (XEXP (op0, 1)) == INTVAL (XEXP (XEXP (op0, 0), 1))
+         && INTVAL (XEXP (op0, 1)) == INTVAL (XEXP (XEXP (op1, 0), 1))
+         && (INTVAL (XEXP (op0, 1))
+             == (GET_MODE_BITSIZE (GET_MODE (op0))
+                 - (GET_MODE_BITSIZE
+                    (GET_MODE (SUBREG_REG (XEXP (XEXP (op0, 0), 0))))))))
+       {
+         op0 = SUBREG_REG (XEXP (XEXP (op0, 0), 0));
+         op1 = SUBREG_REG (XEXP (XEXP (op1, 0), 0));
+       }
+#endif
+
       /* 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 known to be zero for both inputs and if the type of
@@ -7903,8 +8549,7 @@ simplify_comparison (code, pop0, pop1)
       if (GET_CODE (op0) == GET_CODE (op1)
          && GET_MODE_BITSIZE (GET_MODE (op0)) <= HOST_BITS_PER_WIDE_INT
          && ((GET_CODE (op0) == ROTATE && (code == NE || code == EQ))
-             || ((GET_CODE (op0) == LSHIFTRT
-                  || GET_CODE (op0) == ASHIFT || GET_CODE (op0) == LSHIFT)
+             || ((GET_CODE (op0) == LSHIFTRT || GET_CODE (op0) == ASHIFT)
                  && (code != GT && code != LT && code != GE && code != LE))
              || (GET_CODE (op0) == ASHIFTRT
                  && (code != GTU && code != LTU
@@ -7920,7 +8565,7 @@ simplify_comparison (code, pop0, pop1)
 
          if (GET_CODE (op0) == LSHIFTRT || GET_CODE (op0) == ASHIFTRT)
            mask &= (mask >> shift_count) << shift_count;
-         else if (GET_CODE (op0) == ASHIFT || GET_CODE (op0) == LSHIFT)
+         else if (GET_CODE (op0) == ASHIFT)
            mask = (mask & (mask << shift_count)) >> shift_count;
 
          if ((nonzero_bits (XEXP (op0, 0), mode) & ~ mask) == 0
@@ -7964,6 +8609,15 @@ simplify_comparison (code, pop0, pop1)
             the original sign bit. */
          code = unsigned_condition (code);
        }
+
+      /* If both operands are NOT, we can strip off the outer operation
+        and adjust the comparison code for swapped operands; similarly for
+        NEG, except that this must be an equality comparison.  */
+      else if ((GET_CODE (op0) == NOT && GET_CODE (op1) == NOT)
+              || (GET_CODE (op0) == NEG && GET_CODE (op1) == NEG
+                  && (code == EQ || code == NE)))
+       op0 = XEXP (op0, 0), op1 = XEXP (op1, 0), code = swap_condition (code);
+
       else
        break;
     }
@@ -8174,6 +8828,15 @@ simplify_comparison (code, pop0, pop1)
       unsigned_comparison_p = (code == LTU || code == LEU || code == GTU
                               || code == LEU);
 
+      /* If this is a sign bit comparison and we can do arithmetic in
+        MODE, say that we will only be needing the sign bit of OP0.  */
+      if (sign_bit_comparison_p
+         && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
+       op0 = force_to_mode (op0, mode,
+                            ((HOST_WIDE_INT) 1
+                             << (GET_MODE_BITSIZE (mode) - 1)),
+                            NULL_RTX, 0);
+
       /* Now try cases based on the opcode of OP0.  If none of the cases
         does a "continue", we exit this loop immediately after the
         switch.  */
@@ -8351,7 +9014,7 @@ simplify_comparison (code, pop0, pop1)
              && INTVAL (XEXP (SUBREG_REG (op0), 1)) < 0
              && (- INTVAL (XEXP (SUBREG_REG (op0), 1))
                  < GET_MODE_MASK (mode) / 2)
-             && (unsigned) const_op < 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))
@@ -8512,8 +9175,7 @@ simplify_comparison (code, pop0, pop1)
          /* Convert (and (xshift 1 X) Y) to (and (lshiftrt Y X) 1).  This
             will be converted to a ZERO_EXTRACT later.  */
          if (const_op == 0 && equality_comparison_p
-             && (GET_CODE (XEXP (op0, 0)) == ASHIFT
-                 || GET_CODE (XEXP (op0, 0)) == LSHIFT)
+             && GET_CODE (XEXP (op0, 0)) == ASHIFT
              && XEXP (XEXP (op0, 0), 0) == const1_rtx)
            {
              op0 = simplify_and_const_int
@@ -8580,8 +9242,7 @@ simplify_comparison (code, pop0, pop1)
          break;
 
        case ASHIFT:
-       case LSHIFT:
-         /* If we have (compare (xshift FOO N) (const_int C)) and
+         /* If we have (compare (ashift FOO N) (const_int C)) and
             the high order N bits of FOO (N+1 if an inequality comparison)
             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
@@ -8591,7 +9252,7 @@ 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
              && (nonzero_bits (XEXP (op0, 0), mode)
                  & ~ (mask >> (INTVAL (XEXP (op0, 1))
@@ -8795,18 +9456,28 @@ reversible_comparison_p (x)
      rtx x;
 {
   if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
+      || flag_fast_math
       || GET_CODE (x) == NE || GET_CODE (x) == EQ)
     return 1;
 
   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:
+      /* If the mode of the condition codes tells us that this is safe,
+        we need look no further.  */
+      if (REVERSIBLE_CC_MODE (GET_MODE (XEXP (x, 0))))
+       return 1;
+
+      /* Otherwise try and find where the condition codes were last set and
+        use that.  */
       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;
@@ -8875,13 +9546,17 @@ record_value_for_reg (reg, insn, value)
     }
 
   /* For each register modified, show we don't know its value, that
-     its value has been updated, and that we don't know the location of
-     the death of the register.  */
+     we don't know about its bitwise content, that its value has been
+     updated, and that we don't know the location of the death of the
+     register.  */
   for (i = regno; i < endregno; i ++)
     {
       if (insn)
        reg_last_set[i] = insn;
       reg_last_set_value[i] = 0;
+      reg_last_set_mode[i] = 0;
+      reg_last_set_nonzero_bits[i] = 0;
+      reg_last_set_sign_bit_copies[i] = 0;
       reg_last_death[i] = 0;
     }
 
@@ -8923,6 +9598,7 @@ record_value_for_reg (reg, insn, 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]
@@ -8967,9 +9643,11 @@ record_dead_and_set_regs_1 (dest, setter)
    for the things done by INSN.  This is the last thing done in processing
    INSN in the combiner loop.
 
-   We update reg_last_set, reg_last_set_value, reg_last_death, and also the
-   similar information mem_last_set (which insn most recently modified memory)
-   and last_call_cuid (which insn was the most recent subroutine call).  */
+   We update reg_last_set, reg_last_set_value, reg_last_set_mode,
+   reg_last_set_nonzero_bits, reg_last_set_sign_bit_copies, reg_last_death,
+   and also the similar information mem_last_set (which insn most recently
+   modified memory) and last_call_cuid (which insn was the most recent
+   subroutine call).  */
 
 static void
 record_dead_and_set_regs (insn)
@@ -9002,6 +9680,9 @@ record_dead_and_set_regs (insn)
        if (call_used_regs[i])
          {
            reg_last_set_value[i] = 0;
+           reg_last_set_mode[i] = 0;
+           reg_last_set_nonzero_bits[i] = 0;
+           reg_last_set_sign_bit_copies[i] = 0;
            reg_last_death[i] = 0;
          }
 
@@ -9109,10 +9790,15 @@ get_last_value (x)
     {
       rtx insn, set;
 
-      for (insn = prev_nonnote_insn (subst_insn);
-          insn && INSN_CUID (insn) >= subst_low_cuid;
-          insn = prev_nonnote_insn (insn))
-       ;
+      /* If there is an insn that is supposed to be immediately
+        in front of subst_insn, use it.  */
+      if (subst_prev_insn != 0)
+       insn = subst_prev_insn;
+      else
+       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
@@ -9164,14 +9850,20 @@ use_crosses_set_p (x, from_cuid)
   if (code == REG)
     {
       register int regno = REGNO (x);
+      int endreg = regno + (regno < FIRST_PSEUDO_REGISTER
+                           ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1);
+      
 #ifdef PUSH_ROUNDING
       /* Don't allow uses of the stack pointer to be moved,
         because we don't know whether the move crosses a push insn.  */
       if (regno == STACK_POINTER_REGNUM)
        return 1;
 #endif
-      return (reg_last_set[regno]
-             && INSN_CUID (reg_last_set[regno]) > from_cuid);
+      for (;regno < endreg; regno++)
+       if (reg_last_set[regno]
+           && INSN_CUID (reg_last_set[regno]) > from_cuid)
+         return 1;
+      return 0;
     }
 
   if (code == MEM && mem_last_set > from_cuid)
@@ -9229,7 +9921,8 @@ reg_dead_at_p_1 (dest, x)
    We scan backwards from INSN.  If we hit a REG_DEAD note or a CLOBBER
    referencing REG, it is dead.  If we hit a SET referencing REG, it is
    live.  Otherwise, see if it is live or dead at the start of the basic
-   block we are in.  */
+   block we are in.  Hard regs marked as being live in NEWPAT_USED_REGS
+   must be assumed to be always live.  */
 
 static int
 reg_dead_at_p (reg, insn)
@@ -9247,6 +9940,14 @@ reg_dead_at_p (reg, insn)
 
   reg_dead_flag = 0;
 
+  /* Check that reg isn't mentioned in NEWPAT_USED_REGS.  */
+  if (reg_dead_regno < FIRST_PSEUDO_REGISTER)
+    {
+      for (i = reg_dead_regno; i < reg_dead_endregno; i++)
+       if (TEST_HARD_REG_BIT (newpat_used_regs, i))
+         return 0;
+    }
+
   /* Scan backwards until we find a REG_DEAD note, SET, CLOBBER, label, or
      beginning of function.  */
   for (; insn && GET_CODE (insn) != CODE_LABEL;
@@ -9281,6 +9982,106 @@ reg_dead_at_p (reg, insn)
   return 1;
 }
 \f
+/* Note hard registers in X that are used.  This code is similar to
+   that in flow.c, but much simpler since we don't care about pseudos.  */
+
+static void
+mark_used_regs_combine (x)
+     rtx x;
+{
+  register RTX_CODE code = GET_CODE (x);
+  register int regno;
+  int i;
+
+  switch (code)
+    {
+    case LABEL_REF:
+    case SYMBOL_REF:
+    case CONST_INT:
+    case CONST:
+    case CONST_DOUBLE:
+    case PC:
+    case ADDR_VEC:
+    case ADDR_DIFF_VEC:
+    case ASM_INPUT:
+#ifdef HAVE_cc0
+    /* CC0 must die in the insn after it is set, so we don't need to take
+       special note of it here.  */
+    case CC0:
+#endif
+      return;
+
+    case CLOBBER:
+      /* If we are clobbering a MEM, mark any hard registers inside the
+        address as used.  */
+      if (GET_CODE (XEXP (x, 0)) == MEM)
+       mark_used_regs_combine (XEXP (XEXP (x, 0), 0));
+      return;
+
+    case REG:
+      regno = REGNO (x);
+      /* A hard reg in a wide mode may really be multiple registers.
+        If so, mark all of them just like the first.  */
+      if (regno < FIRST_PSEUDO_REGISTER)
+       {
+         /* None of this applies to the stack, frame or arg pointers */
+         if (regno == STACK_POINTER_REGNUM
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+             || regno == HARD_FRAME_POINTER_REGNUM
+#endif
+#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+             || (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
+#endif
+             || regno == FRAME_POINTER_REGNUM)
+           return;
+
+         i = HARD_REGNO_NREGS (regno, GET_MODE (x));
+         while (i-- > 0)
+           SET_HARD_REG_BIT (newpat_used_regs, regno + i);
+       }
+      return;
+
+    case SET:
+      {
+       /* If setting a MEM, or a SUBREG of a MEM, then note any hard regs in
+          the address.  */
+       register rtx testreg = SET_DEST (x);
+
+       while (GET_CODE (testreg) == SUBREG
+              || GET_CODE (testreg) == ZERO_EXTRACT
+              || GET_CODE (testreg) == SIGN_EXTRACT
+              || GET_CODE (testreg) == STRICT_LOW_PART)
+         testreg = XEXP (testreg, 0);
+
+       if (GET_CODE (testreg) == MEM)
+         mark_used_regs_combine (XEXP (testreg, 0));
+
+       mark_used_regs_combine (SET_SRC (x));
+       return;
+      }
+    }
+
+  /* Recursively scan the operands of this expression.  */
+
+  {
+    register char *fmt = GET_RTX_FORMAT (code);
+
+    for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+      {
+        if (fmt[i] == 'e')
+         mark_used_regs_combine (XEXP (x, i));
+        else if (fmt[i] == 'E')
+          {
+            register int j;
+
+            for (j = 0; j < XVECLEN (x, i); j++)
+              mark_used_regs_combine (XVECEXP (x, i, j));
+          }
+      }
+  }
+}
+
+\f
 /* Remove register number REGNO from the dead registers list of INSN.
 
    Return the note used to record the death, if there was one.  */
@@ -9354,7 +10155,7 @@ move_deaths (x, from_cuid, to_insn, pnotes)
                if (i < regno || i >= ourend)
                  REG_NOTES (where_dead)
                    = gen_rtx (EXPR_LIST, REG_DEAD,
-                              gen_rtx (REG, word_mode, i),
+                              gen_rtx (REG, reg_raw_mode[i], i),
                               REG_NOTES (where_dead));
            }
 
@@ -9510,9 +10311,26 @@ distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1)
       switch (REG_NOTE_KIND (note))
        {
        case REG_UNUSED:
+         /* If this note is from any insn other than i3, then we have no
+            use for it, and must ignore it.
+
+            Any clobbers for i3 may still exist, and so we must process
+            REG_UNUSED notes from that insn.
+
+            Any clobbers from i2 or i1 can only exist if they were added by
+            recog_for_combine.  In that case, recog_for_combine created the
+            necessary REG_UNUSED notes.  Trying to keep any original
+            REG_UNUSED notes from these insns can cause incorrect output
+            if it is for the same register as the original i3 dest.
+            In that case, we will notice that the register is set in i3,
+            and then add a REG_UNUSED note for the destination of i3, which
+            is wrong.  */
+         if (from_insn != i3)
+           break;
+
          /* If this register is set or clobbered in I3, put the note there
             unless there is one already.  */
-         if (reg_set_p (XEXP (note, 0), PATTERN (i3)))
+         else if (reg_set_p (XEXP (note, 0), PATTERN (i3)))
            {
              if (! (GET_CODE (XEXP (note, 0)) == REG
                     ? find_regno_note (i3, REG_UNUSED, REGNO (XEXP (note, 0)))
@@ -9616,7 +10434,11 @@ distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1)
             In both cases, we must search to see if we can find a previous
             use of A and put the death note there.  */
 
-         if (reg_referenced_p (XEXP (note, 0), PATTERN (i3)))
+         if (from_insn
+             && GET_CODE (from_insn) == CALL_INSN
+              && find_reg_fusage (from_insn, USE, XEXP (note, 0)))
+           place = from_insn;
+         else if (reg_referenced_p (XEXP (note, 0), PATTERN (i3)))
            place = i3;
          else if (i2 != 0 && next_nonnote_insn (i2) == i3
                   && reg_referenced_p (XEXP (note, 0), PATTERN (i2)))
@@ -9768,6 +10590,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
@@ -9870,8 +10702,8 @@ distribute_links (links)
         since most links don't point very far away.  */
 
       for (insn = NEXT_INSN (XEXP (link, 0));
-          (insn && GET_CODE (insn) != CODE_LABEL
-           && GET_CODE (PREV_INSN (insn)) != JUMP_INSN);
+          (insn && (this_basic_block == n_basic_blocks - 1
+                    || basic_block_head[this_basic_block + 1] != insn));
           insn = NEXT_INSN (insn))
        if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
            && reg_overlap_mentioned_p (reg, PATTERN (insn)))
@@ -9880,6 +10712,12 @@ distribute_links (links)
              place = insn;
            break;
          }
+       else if (GET_CODE (insn) == CALL_INSN
+             && find_reg_fusage (insn, USE, reg))
+         {
+           place = insn;
+           break;
+         }
 
       /* If we found a place to put the link, place it there unless there
         is already a link to the same insn as LINK at that point.  */
@@ -9896,6 +10734,12 @@ distribute_links (links)
            {
              XEXP (link, 1) = LOG_LINKS (place);
              LOG_LINKS (place) = link;
+
+             /* Set added_links_insn to the earliest insn we added a
+                link to.  */
+             if (added_links_insn == 0 
+                 || INSN_CUID (added_links_insn) > INSN_CUID (place))
+               added_links_insn = place;
            }
        }
     }