OSDN Git Service

[pf3gnuchains/gcc-fork.git] / gcc / combine.c
index 017cf12..bd0a225 100644 (file)
@@ -75,27 +75,21 @@ Boston, MA 02111-1307, USA.  */
    combine anyway.  */
 
 #include "config.h"
-#ifdef __STDC__
-#include <stdarg.h>
-#else
-#include <varargs.h>
-#endif
-
-/* Must precede rtl.h for FFS.  */
-#include <stdio.h>
-
-#include "rtl.h"
+#include "system.h"
+#include "rtl.h" /* stdio.h must precede rtl.h for FFS.  */
 #include "flags.h"
 #include "regs.h"
 #include "hard-reg-set.h"
-#include "expr.h"
 #include "basic-block.h"
 #include "insn-config.h"
+/* Include expr.h after insn-config.h so we get HAVE_conditional_move. */
+#include "expr.h"
 #include "insn-flags.h"
 #include "insn-codes.h"
 #include "insn-attr.h"
 #include "recog.h"
 #include "real.h"
+#include "toplev.h"
 
 /* It is not safe to use ordinary gen_lowpart in combine.
    Use gen_lowpart_for_combine instead.  See comments there.  */
@@ -399,6 +393,7 @@ static void init_reg_last_arrays    PROTO((void));
 static void setup_incoming_promotions   PROTO((void));
 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 sets_function_arg_p PROTO((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));
@@ -431,7 +426,7 @@ static int merge_outer_ops  PROTO((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 *, 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,
                                  ...));
@@ -679,6 +674,9 @@ combine_instructions (f, nregs)
   total_successes += combine_successes;
 
   nonzero_sign_valid = 0;
+
+  /* Make recognizer allow volatile MEMs again.  */
+  init_recog ();
 }
 
 /* Wipe the reg_last_xxx arrays in preparation for another pass.  */
@@ -821,7 +819,8 @@ static int
 can_combine_p (insn, i3, pred, succ, pdest, psrc)
      rtx insn;
      rtx i3;
-     rtx pred, succ;
+     rtx pred ATTRIBUTE_UNUSED;
+     rtx succ;
      rtx *pdest, *psrc;
 {
   int i;
@@ -860,6 +859,43 @@ can_combine_p (insn, i3, pred, succ, pdest, psrc)
 
          switch (GET_CODE (elt))
            {
+           /* This is important to combine floating point insns
+              for the SH4 port.  */
+           case USE:
+             /* Combining an isolated USE doesn't make sense.
+                We depend here on combinable_i3_pat to reject them.  */
+             /* The code below this loop only verifies that the inputs of
+                the SET in INSN do not change.  We call reg_set_between_p
+                to verify that the REG in the USE does not change betweeen
+                I3 and INSN.
+                If the USE in INSN was for a pseudo register, the matching
+                insn pattern will likely match any register; combining this
+                with any other USE would only be safe if we knew that the
+                used registers have identical values, or if there was
+                something to tell them apart, e.g. different modes.  For
+                now, we forgo such compilcated tests and simply disallow
+                combining of USES of pseudo registers with any other USE.  */
+             if (GET_CODE (XEXP (elt, 0)) == REG
+                 && GET_CODE (PATTERN (i3)) == PARALLEL)
+               {
+                 rtx i3pat = PATTERN (i3);
+                 int i = XVECLEN (i3pat, 0) - 1;
+                 int regno = REGNO (XEXP (elt, 0));
+                 do
+                   {
+                     rtx i3elt = XVECEXP (i3pat, 0, i);
+                     if (GET_CODE (i3elt) == USE
+                         && GET_CODE (XEXP (i3elt, 0)) == REG
+                         && (REGNO (XEXP (i3elt, 0)) == regno
+                             ? reg_set_between_p (XEXP (elt, 0),
+                                                  PREV_INSN (insn), i3)
+                             : regno >= FIRST_PSEUDO_REGISTER))
+                       return 0;
+                   }
+                 while (--i >= 0);
+               }
+             break;
+
              /* We can ignore CLOBBERs.  */
            case CLOBBER:
              break;
@@ -918,8 +954,14 @@ can_combine_p (insn, i3, pred, succ, pdest, psrc)
       /* Don't substitute into an incremented register.  */
       || FIND_REG_INC_NOTE (i3, dest)
       || (succ && FIND_REG_INC_NOTE (succ, dest))
+#if 0
       /* Don't combine the end of a libcall into anything.  */
+      /* ??? This gives worse code, and appears to be unnecessary, since no
+        pass after flow uses REG_LIBCALL/REG_RETVAL notes.  Local-alloc does
+        use REG_RETVAL notes for noconflict blocks, but other code here
+        makes sure that those insns don't disappear.  */
       || find_reg_note (insn, REG_RETVAL, NULL_RTX)
+#endif
       /* Make sure that DEST is not used after SUCC but before I3.  */
       || (succ && ! all_adjacent
          && reg_used_between_p (dest, succ, i3))
@@ -1000,14 +1042,19 @@ can_combine_p (insn, i3, pred, succ, pdest, psrc)
        return 0;
 
   /* If INSN contains anything volatile, or is an `asm' (whether volatile
-     or not), reject, unless nothing volatile comes between it and I3,
-     with the exception of SUCC.  */
+     or not), reject, unless nothing volatile comes between it and I3 */
 
   if (GET_CODE (src) == ASM_OPERANDS || volatile_refs_p (src))
-    for (p = NEXT_INSN (insn); p != i3; p = NEXT_INSN (p))
-      if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
-         && p != succ && volatile_refs_p (PATTERN (p)))
-       return 0;
+    {
+      /* Make sure succ doesn't contain a volatile reference.  */
+      if (succ != 0 && volatile_refs_p (PATTERN (succ)))
+        return 0;
+  
+      for (p = NEXT_INSN (insn); p != i3; p = NEXT_INSN (p))
+        if (GET_RTX_CLASS (GET_CODE (p)) == 'i'
+         && p != succ && volatile_refs_p (PATTERN (p)))
+       return 0;
+    }
 
   /* If INSN is an asm, and DEST is a hard register, reject, since it has
      to be an explicit register variable, and was chosen for a reason.  */
@@ -1161,7 +1208,11 @@ combinable_i3pat (i3, loc, i2dest, i1dest, i1_not_in_src, pi3dest_killed)
       rtx set = expand_field_assignment (x);
       rtx dest = SET_DEST (set);
       rtx src = SET_SRC (set);
-      rtx inner_dest = dest, inner_src = src;
+      rtx inner_dest = dest;
+#if 0
+      rtx inner_src = src;
+#endif
 
       SUBST (*loc, set);
 
@@ -1305,8 +1356,6 @@ try_combine (i3, i2, i1)
   int i3_subst_into_i2 = 0;
   /* Notes that I1, I2 or I3 is a MULT operation.  */
   int have_mult = 0;
-  /* Number of clobbers of SCRATCH we had to add.  */
-  int i3_scratches = 0, i2_scratches = 0, other_scratches = 0;
 
   int maxreg;
   rtx temp;
@@ -1322,7 +1371,12 @@ try_combine (i3, i2, i1)
   if (GET_RTX_CLASS (GET_CODE (i3)) != 'i'
       || GET_RTX_CLASS (GET_CODE (i2)) != 'i'
       || (i1 && GET_RTX_CLASS (GET_CODE (i1)) != 'i')
-      || find_reg_note (i3, REG_LIBCALL, NULL_RTX))
+#if 0
+      /* ??? This gives worse code, and appears to be unnecessary, since no
+        pass after flow uses REG_LIBCALL/REG_RETVAL notes.  */
+      || find_reg_note (i3, REG_LIBCALL, NULL_RTX)
+#endif
+)
     return 0;
 
   combine_attempts++;
@@ -1621,8 +1675,10 @@ try_combine (i3, i2, i1)
       && XEXP (SET_SRC (PATTERN (i3)), 1) == const0_rtx
       && rtx_equal_p (XEXP (SET_SRC (PATTERN (i3)), 0), i2dest))
     {
+#ifdef EXTRA_CC_MODES
       rtx *cc_use;
       enum machine_mode compare_mode;
+#endif
 
       newpat = PATTERN (i3);
       SUBST (XEXP (SET_SRC (newpat), 0), i2src);
@@ -1776,8 +1832,7 @@ try_combine (i3, i2, i1)
   mark_used_regs_combine (newpat);
 
   /* Is the result of combination a valid instruction?  */
-  insn_code_number
-    = recog_for_combine (&newpat, i3, &new_i3_notes, &i3_scratches);
+  insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
 
   /* If the result isn't valid, see if it is a PARALLEL of two SETs where
      the second SET's destination is a register that is unused.  In that case,
@@ -1798,8 +1853,7 @@ try_combine (i3, i2, i1)
       && asm_noperands (newpat) < 0)
     {
       newpat = XVECEXP (newpat, 0, 0);
-      insn_code_number
-       = recog_for_combine (&newpat, i3, &new_i3_notes, &i3_scratches);
+      insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
     }
 
   else if (insn_code_number < 0 && GET_CODE (newpat) == PARALLEL
@@ -1812,8 +1866,7 @@ try_combine (i3, i2, i1)
           && asm_noperands (newpat) < 0)
     {
       newpat = XVECEXP (newpat, 0, 1);
-      insn_code_number
-       = recog_for_combine (&newpat, i3, &new_i3_notes, &i3_scratches);
+      insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
     }
 
   /* If we were combining three insns and the result is a simple SET
@@ -1882,8 +1935,7 @@ try_combine (i3, i2, i1)
          if (REGNO (i2dest) >= FIRST_PSEUDO_REGISTER)
            SUBST (regno_reg_rtx[REGNO (i2dest)], ni2dest);
 
-         i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes,
-                                             &i2_scratches);
+         i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
 
          /* If I2 or I3 has multiple SETs, we won't know how to track
             register status, so don't use these insns.  If I2's destination
@@ -1892,8 +1944,8 @@ try_combine (i3, i2, i1)
          if (i2_code_number >= 0 && i2set && i3set
              && (next_real_insn (i2) == i3
                  || ! reg_used_between_p (SET_DEST (i2set), i2, i3)))
-           insn_code_number = recog_for_combine (&newi3pat, i3, &new_i3_notes,
-                                                 &i3_scratches); 
+           insn_code_number = recog_for_combine (&newi3pat, i3,
+                                                 &new_i3_notes);
          if (insn_code_number >= 0)
            newpat = newi3pat;
 
@@ -1980,14 +2032,12 @@ try_combine (i3, i2, i1)
 
          newi2pat = gen_rtx_combine (SET, VOIDmode, newdest, *split);
          SUBST (*split, newdest);
-         i2_code_number
-           = recog_for_combine (&newi2pat, i2, &new_i2_notes, &i2_scratches);
+         i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
 
          /* If the split point was a MULT and we didn't have one before,
             don't use one now.  */
          if (i2_code_number >= 0 && ! (split_code == MULT && ! have_mult))
-           insn_code_number
-             = recog_for_combine (&newpat, i3, &new_i3_notes, &i3_scratches);
+           insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
        }
     }
 
@@ -2041,12 +2091,10 @@ try_combine (i3, i2, i1)
       newpat = XVECEXP (newpat, 0, 1);
       SUBST (SET_SRC (newpat),
             gen_lowpart_for_combine (GET_MODE (SET_SRC (newpat)), ni2dest));
-      i2_code_number
-       = recog_for_combine (&newi2pat, i2, &new_i2_notes, &i2_scratches);
+      i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
 
       if (i2_code_number >= 0)
-       insn_code_number
-         = recog_for_combine (&newpat, i3, &new_i3_notes, &i3_scratches);
+       insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
 
       if (insn_code_number >= 0)
        {
@@ -2133,12 +2181,10 @@ try_combine (i3, i2, i1)
          newpat = XVECEXP (newpat, 0, 0);
        }
 
-      i2_code_number
-       = recog_for_combine (&newi2pat, i2, &new_i2_notes, &i2_scratches);
+      i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
 
       if (i2_code_number >= 0)
-       insn_code_number
-         = recog_for_combine (&newpat, i3, &new_i3_notes, &i3_scratches);
+       insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
     }
 
   /* If it still isn't recognized, fail and change things back the way they
@@ -2160,9 +2206,8 @@ try_combine (i3, i2, i1)
 
       CLEAR_HARD_REG_SET (newpat_used_regs);
 
-      other_code_number
-       = recog_for_combine (&other_pat, undobuf.other_insn,
-                            &new_other_notes, &other_scratches);
+      other_code_number = recog_for_combine (&other_pat, undobuf.other_insn,
+                                            &new_other_notes);
 
       if (other_code_number < 0 && ! check_asm_operands (other_pat))
        {
@@ -2479,12 +2524,6 @@ try_combine (i3, i2, i1)
     if (newi2pat)
       note_stores (newi2pat, set_nonzero_bits_and_sign_copies);
 
-    /* If we added any (clobber (scratch)), add them to the max for a
-       block.  This is a very pessimistic calculation, since we might
-       have had them already and this might not be the worst block, but
-       it's not worth doing any better.  */
-    max_scratch += i3_scratches + i2_scratches + other_scratches;
-
     /* If I3 is now an unconditional jump, ensure that it has a 
        BARRIER following it since it may have initially been a
        conditional jump.  It may also be the last nonnote insn.  */
@@ -2696,7 +2735,7 @@ find_split_point (loc, insn)
          if (BITS_BIG_ENDIAN)
            pos = GET_MODE_BITSIZE (mode) - len - pos;
 
-         if (src == mask)
+         if ((unsigned HOST_WIDE_INT) src == mask)
            SUBST (SET_SRC (x),
                   gen_binary (IOR, mode, dest, GEN_INT (src << pos)));
          else
@@ -3009,103 +3048,154 @@ subst (x, from, to, in_dest, unique_copy)
   if (COMBINE_RTX_EQUAL_P (x, to))
     return to;
 
-  len = GET_RTX_LENGTH (code);
-  fmt = GET_RTX_FORMAT (code);
+  /* Parallel asm_operands need special attention because all of the
+     inputs are shared across the arms.  Furthermore, unsharing the
+     rtl results in recognition failures.  Failure to handle this case
+     specially can result in circular rtl.
 
-  /* We don't need to process a SET_DEST that is a register, CC0, or PC, so
-     set up to skip this common case.  All other cases where we want to
-     suppress replacing something inside a SET_SRC are handled via the
-     IN_DEST operand.  */
-  if (code == SET
-      && (GET_CODE (SET_DEST (x)) == REG
-        || GET_CODE (SET_DEST (x)) == CC0
-        || GET_CODE (SET_DEST (x)) == PC))
-    fmt = "ie";
-
-  /* Get the mode of operand 0 in case X is now a SIGN_EXTEND of a
-     constant.  */
-  if (fmt[0] == 'e')
-    op0_mode = GET_MODE (XEXP (x, 0));
+     Solve this by doing a normal pass across the first entry of the
+     parallel, and only processing the SET_DESTs of the subsequent
+     entries.  Ug.  */
 
-  for (i = 0; i < len; i++)
+  if (code == PARALLEL
+      && GET_CODE (XVECEXP (x, 0, 0)) == SET
+      && GET_CODE (SET_SRC (XVECEXP (x, 0, 0))) == ASM_OPERANDS)
     {
-      if (fmt[i] == 'E')
+      new = subst (XVECEXP (x, 0, 0), from, to, 0, unique_copy);
+
+      /* If this substitution failed, this whole thing fails.  */
+      if (GET_CODE (new) == CLOBBER
+         && XEXP (new, 0) == const0_rtx)
+       return new;
+
+      SUBST (XVECEXP (x, 0, 0), new);
+
+      for (i = XVECLEN (x, 0) - 1; i >= 1; i--)
        {
-         register int j;
-         for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+         rtx dest = SET_DEST (XVECEXP (x, 0, i));
+         
+         if (GET_CODE (dest) != REG
+             && GET_CODE (dest) != CC0
+             && GET_CODE (dest) != PC)
            {
-             if (COMBINE_RTX_EQUAL_P (XVECEXP (x, i, j), from))
-               {
-                 new = (unique_copy && n_occurrences ? copy_rtx (to) : to);
-                 n_occurrences++;
-               }
-             else
-               {
-                 new = subst (XVECEXP (x, i, j), from, to, 0, unique_copy);
+             new = subst (dest, from, to, 0, unique_copy);
 
-                 /* If this substitution failed, this whole thing fails.  */
-                 if (GET_CODE (new) == CLOBBER && XEXP (new, 0) == const0_rtx)
-                   return new;
-               }
+             /* If this substitution failed, this whole thing fails.  */
+             if (GET_CODE (new) == CLOBBER
+                 && XEXP (new, 0) == const0_rtx)
+               return new;
 
-             SUBST (XVECEXP (x, i, j), new);
+             SUBST (SET_DEST (XVECEXP (x, 0, i)), new);
            }
        }
-      else if (fmt[i] == 'e')
+    }
+  else
+    {
+      len = GET_RTX_LENGTH (code);
+      fmt = GET_RTX_FORMAT (code);
+
+      /* We don't need to process a SET_DEST that is a register, CC0,
+        or PC, so set up to skip this common case.  All other cases
+        where we want to suppress replacing something inside a
+        SET_SRC are handled via the IN_DEST operand.  */
+      if (code == SET
+         && (GET_CODE (SET_DEST (x)) == REG
+             || GET_CODE (SET_DEST (x)) == CC0
+             || GET_CODE (SET_DEST (x)) == PC))
+       fmt = "ie";
+
+      /* Get the mode of operand 0 in case X is now a SIGN_EXTEND of a
+        constant.  */
+      if (fmt[0] == 'e')
+       op0_mode = GET_MODE (XEXP (x, 0));
+
+      for (i = 0; i < len; i++)
        {
-         if (COMBINE_RTX_EQUAL_P (XEXP (x, i), from))
+         if (fmt[i] == 'E')
            {
-             /* 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))))
+             register int j;
+             for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+               {
+                 if (COMBINE_RTX_EQUAL_P (XVECEXP (x, i, j), from))
+                   {
+                     new = (unique_copy && n_occurrences
+                            ? copy_rtx (to) : to);
+                     n_occurrences++;
+                   }
+                 else
+                   {
+                     new = subst (XVECEXP (x, i, j), from, to, 0,
+                                  unique_copy);
+
+                     /* If this substitution failed, this whole thing
+                        fails.  */
+                     if (GET_CODE (new) == CLOBBER
+                         && XEXP (new, 0) == const0_rtx)
+                       return new;
+                   }
+
+                 SUBST (XVECEXP (x, i, j), new);
+               }
+           }
+         else if (fmt[i] == 'e')
+           {
+             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)
+                     && ! (code == SET && i == 1 && XEXP (x, 0) == cc0_rtx)
 #endif
-                 )
-               return gen_rtx_CLOBBER (VOIDmode, const0_rtx);
+                     )
+                   return gen_rtx_CLOBBER (VOIDmode, const0_rtx);
 
-             new = (unique_copy && n_occurrences ? copy_rtx (to) : to);
-             n_occurrences++;
+                 new = (unique_copy && n_occurrences ? copy_rtx (to) : to);
+                 n_occurrences++;
+               }
+             else
+               /* If we are in a SET_DEST, suppress most cases unless we
+                  have gone inside a MEM, in which case we want to
+                  simplify the address.  We assume here that things that
+                  are actually part of the destination have their inner
+                  parts in the first expression.  This is true for SUBREG, 
+                  STRICT_LOW_PART, and ZERO_EXTRACT, which are the only
+                  things aside from REG and MEM that should appear in a
+                  SET_DEST.  */
+               new = subst (XEXP (x, i), from, to,
+                            (((in_dest
+                               && (code == SUBREG || code == STRICT_LOW_PART
+                                   || code == ZERO_EXTRACT))
+                              || code == SET)
+                             && i == 0), unique_copy);
+
+             /* If we found that we will have to reject this combination,
+                indicate that by returning the CLOBBER ourselves, rather than
+                an expression containing it.  This will speed things up as
+                well as prevent accidents where two CLOBBERs are considered
+                to be equal, thus producing an incorrect simplification.  */
+
+             if (GET_CODE (new) == CLOBBER && XEXP (new, 0) == const0_rtx)
+               return new;
+
+             SUBST (XEXP (x, i), new);
            }
-         else
-           /* If we are in a SET_DEST, suppress most cases unless we
-              have gone inside a MEM, in which case we want to
-              simplify the address.  We assume here that things that
-              are actually part of the destination have their inner
-              parts in the first expression.  This is true for SUBREG, 
-              STRICT_LOW_PART, and ZERO_EXTRACT, which are the only
-              things aside from REG and MEM that should appear in a
-              SET_DEST.  */
-           new = subst (XEXP (x, i), from, to,
-                        (((in_dest
-                           && (code == SUBREG || code == STRICT_LOW_PART
-                               || code == ZERO_EXTRACT))
-                          || code == SET)
-                         && i == 0), unique_copy);
-
-         /* If we found that we will have to reject this combination,
-            indicate that by returning the CLOBBER ourselves, rather than
-            an expression containing it.  This will speed things up as
-            well as prevent accidents where two CLOBBERs are considered
-            to be equal, thus producing an incorrect simplification.  */
-
-         if (GET_CODE (new) == CLOBBER && XEXP (new, 0) == const0_rtx)
-           return new;
-
-         SUBST (XEXP (x, i), new);
        }
     }
 
@@ -3688,7 +3778,9 @@ simplify_rtx (x, op0_mode, last, in_dest)
       if (GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)
        break;
 
-      if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
+      if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+         && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
+                                   GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0)))))
        SUBST (XEXP (x, 0),
               force_to_mode (XEXP (x, 0), GET_MODE (XEXP (x, 0)),
                              GET_MODE_MASK (mode), NULL_RTX, 0));
@@ -3717,10 +3809,12 @@ simplify_rtx (x, op0_mode, last, in_dest)
        return SUBREG_REG (XEXP (x, 0));
 
       /* If we know that the value is already truncated, we can
-         replace the TRUNCATE with a SUBREG.  */
-      if (GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))) <= HOST_BITS_PER_WIDE_INT
-         && (nonzero_bits (XEXP (x, 0), GET_MODE (XEXP (x, 0)))
-             &~ GET_MODE_MASK (mode)) == 0)
+         replace the TRUNCATE with a SUBREG if TRULY_NOOP_TRUNCATION is
+        nonzero for the corresponding modes.  */
+      if (TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
+                                GET_MODE_BITSIZE (GET_MODE (XEXP (x, 0))))
+         && num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0)))
+            >= GET_MODE_BITSIZE (mode) + 1)
        return gen_lowpart_for_combine (mode, XEXP (x, 0));
 
       /* A truncate of a comparison can be replaced with a subreg if
@@ -4061,7 +4155,7 @@ simplify_rtx (x, op0_mode, last, in_dest)
          if (new_code == NE && GET_MODE_CLASS (mode) == MODE_INT
              && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
              && ((STORE_FLAG_VALUE & GET_MODE_MASK (mode))
-                 == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))
+                 == (unsigned HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE(mode)-1))
              && op1 == const0_rtx
              && mode == GET_MODE (op0)
              && (i = exact_log2 (nonzero_bits (op0, mode))) >= 0)
@@ -4589,9 +4683,8 @@ simplify_set (x)
              && exact_log2 (mask = nonzero_bits (op0, GET_MODE (op0))) >= 0)
            {
              rtx pat = PATTERN (other_insn), note = 0;
-             int scratches;
 
-             if ((recog_for_combine (&pat, other_insn, &note, &scratches) < 0
+             if ((recog_for_combine (&pat, other_insn, &note) < 0
                   && ! check_asm_operands (pat)))
                {
                  PUT_CODE (*cc_use, old_code);
@@ -4649,7 +4742,7 @@ simplify_set (x)
      we only care about the low bits of the result.
 
      However, on machines without WORD_REGISTER_OPERATIONS defined, we cannot
-     perform a narrower operation that requested since the high-order bits will
+     perform a narrower operation than 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.  */
  
@@ -5028,7 +5121,7 @@ simplify_logical (x, last)
         when STORE_FLAG_VALUE is the sign bit.  */
       if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
          && ((STORE_FLAG_VALUE & GET_MODE_MASK (mode))
-             == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))
+             == (unsigned HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))
          && op1 == const_true_rtx
          && GET_RTX_CLASS (GET_CODE (op0)) == '<'
          && reversible_comparison_p (op0))
@@ -5151,7 +5244,7 @@ expand_compound_operation (x)
          && subreg_lowpart_p (XEXP (x, 0))
          && GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT
          && (nonzero_bits (SUBREG_REG (XEXP (x, 0)), GET_MODE (x))
-             & ~ GET_MODE_MASK (GET_MODE (SUBREG_REG (x)))) == 0)
+             & ~ GET_MODE_MASK (GET_MODE (XEXP (x, 0)))) == 0)
        return SUBREG_REG (XEXP (x, 0));
 
       /* (zero_extend:DI (truncate:SI foo:DI)) is just foo:DI when foo
@@ -5329,6 +5422,24 @@ expand_field_assignment (x)
 
       compute_mode = GET_MODE (inner);
 
+      /* Don't attempt bitwise arithmetic on non-integral modes.  */
+      if (! INTEGRAL_MODE_P (compute_mode))
+       {
+         enum machine_mode imode;
+
+         /* Something is probably seriously wrong if this matches.  */
+         if (! FLOAT_MODE_P (compute_mode))
+           break;
+
+         /* Try to find an integral mode to pun with.  */
+         imode = mode_for_size (GET_MODE_BITSIZE (compute_mode), MODE_INT, 0);
+         if (imode == BLKmode)
+           break;
+
+         compute_mode = imode;
+         inner = gen_lowpart_for_combine (imode, inner);
+       }
+
       /* Compute a mask of LEN bits, if we can do this on the host machine.  */
       if (len < HOST_BITS_PER_WIDE_INT)
        mask = GEN_INT (((HOST_WIDE_INT) 1 << len) - 1);
@@ -5550,27 +5661,45 @@ make_extraction (mode, inner, pos, pos_rtx, len,
 #ifdef HAVE_insv
   if (in_dest)
     {
-      wanted_inner_reg_mode = insn_operand_mode[(int) CODE_FOR_insv][0];
-      pos_mode = insn_operand_mode[(int) CODE_FOR_insv][2];
-      extraction_mode = insn_operand_mode[(int) CODE_FOR_insv][3];
+      wanted_inner_reg_mode
+       = (insn_operand_mode[(int) CODE_FOR_insv][0] == VOIDmode
+          ? word_mode
+          : insn_operand_mode[(int) CODE_FOR_insv][0]);
+      pos_mode = (insn_operand_mode[(int) CODE_FOR_insv][2] == VOIDmode
+                 ? word_mode : insn_operand_mode[(int) CODE_FOR_insv][2]);
+      extraction_mode = (insn_operand_mode[(int) CODE_FOR_insv][3] == VOIDmode
+                        ? word_mode
+                        : insn_operand_mode[(int) CODE_FOR_insv][3]);
     }
 #endif
 
 #ifdef HAVE_extzv
   if (! in_dest && unsignedp)
     {
-      wanted_inner_reg_mode = insn_operand_mode[(int) CODE_FOR_extzv][1];
-      pos_mode = insn_operand_mode[(int) CODE_FOR_extzv][3];
-      extraction_mode = insn_operand_mode[(int) CODE_FOR_extzv][0];
+      wanted_inner_reg_mode
+       = (insn_operand_mode[(int) CODE_FOR_extzv][1] == VOIDmode
+          ? word_mode
+          : insn_operand_mode[(int) CODE_FOR_extzv][1]);
+      pos_mode = (insn_operand_mode[(int) CODE_FOR_extzv][3] == VOIDmode
+                 ? word_mode : insn_operand_mode[(int) CODE_FOR_extzv][3]);
+      extraction_mode = (insn_operand_mode[(int) CODE_FOR_extzv][0] == VOIDmode
+                        ? word_mode
+                        : insn_operand_mode[(int) CODE_FOR_extzv][0]);
     }
 #endif
 
 #ifdef HAVE_extv
   if (! in_dest && ! unsignedp)
     {
-      wanted_inner_reg_mode = insn_operand_mode[(int) CODE_FOR_extv][1];
-      pos_mode = insn_operand_mode[(int) CODE_FOR_extv][3];
-      extraction_mode = insn_operand_mode[(int) CODE_FOR_extv][0];
+      wanted_inner_reg_mode
+       = (insn_operand_mode[(int) CODE_FOR_extv][1] == VOIDmode
+          ? word_mode
+          : insn_operand_mode[(int) CODE_FOR_extv][1]);
+      pos_mode = (insn_operand_mode[(int) CODE_FOR_extv][3] == VOIDmode
+                 ? word_mode : insn_operand_mode[(int) CODE_FOR_extv][3]);
+      extraction_mode = (insn_operand_mode[(int) CODE_FOR_extv][0] == VOIDmode
+                        ? word_mode
+                        : insn_operand_mode[(int) CODE_FOR_extv][0]);
     }
 #endif
 
@@ -5995,6 +6124,23 @@ make_compound_operation (x, in_code)
 
          return newer;
        }
+
+      /* If this is a paradoxical subreg, and the new code is a sign or
+        zero extension, omit the subreg and widen the extension.  If it
+        is a regular subreg, we can still get rid of the subreg by not
+        widening so much, or in fact removing the extension entirely.  */
+      if ((GET_CODE (tem) == SIGN_EXTEND
+          || GET_CODE (tem) == ZERO_EXTEND)
+         && subreg_lowpart_p (x))
+       {
+         if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (tem))
+             || (GET_MODE_SIZE (mode) >
+                 GET_MODE_SIZE (GET_MODE (XEXP (tem, 0)))))
+           tem = gen_rtx_combine (GET_CODE (tem), mode, XEXP (tem, 0));
+         else
+           tem = gen_lowpart_for_combine (mode, XEXP (tem, 0));
+         return tem;
+       }
       break;
       
     default:
@@ -6208,7 +6354,7 @@ force_to_mode (x, mode, mask, reg, just_select)
             need it.  */
 
          if (GET_CODE (x) == AND && GET_CODE (XEXP (x, 1)) == CONST_INT
-             && INTVAL (XEXP (x, 1)) == mask)
+             && (unsigned HOST_WIDE_INT) INTVAL (XEXP (x, 1)) == mask)
            x = XEXP (x, 0);
 
          /* If it remains an AND, try making another AND with the bits
@@ -6429,7 +6575,7 @@ force_to_mode (x, mode, mask, reg, just_select)
       /* 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 (GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT
-         && (mask == ((HOST_WIDE_INT) 1
+         && (mask == ((unsigned HOST_WIDE_INT) 1
                       << (GET_MODE_BITSIZE (GET_MODE (x)) - 1))))
        return force_to_mode (XEXP (x, 0), mode, mask, reg, next_select);
 
@@ -6883,8 +7029,6 @@ rtx_equal_for_field_assignment_p (x, y)
      rtx x;
      rtx y;
 {
-  rtx last_x, last_y;
-
   if (x == y || rtx_equal_p (x, y))
     return 1;
 
@@ -6906,19 +7050,12 @@ rtx_equal_for_field_assignment_p (x, y)
                      gen_lowpart_for_combine (GET_MODE (SUBREG_REG (x)), y)))
     return 1;
 
-  last_x = get_last_value (x);
-  last_y = get_last_value (y);
-
-  return ((last_x != 0
-          && GET_CODE (last_x) != CLOBBER
-          && rtx_equal_for_field_assignment_p (last_x, y))
-         || (last_y != 0
-             && GET_CODE (last_y) != CLOBBER
-             && rtx_equal_for_field_assignment_p (x, last_y))
-         || (last_x != 0 && last_y != 0
-             && GET_CODE (last_x) != CLOBBER
-             && GET_CODE (last_y) != CLOBBER
-             && rtx_equal_for_field_assignment_p (last_x, last_y)));
+  /* We used to see if get_last_value of X and Y were the same but that's
+     not correct.  In one direction, we'll cause the assignment to have
+     the wrong destination and in the case, we'll import a register into this
+     insn that might have already have been dead.   So fail if none of the
+     above cases are true.  */
+  return 0;
 }
 \f
 /* See if X, a SET operation, can be rewritten as a bit-field assignment.
@@ -7012,8 +7149,8 @@ make_field_assignment (x)
 
   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))
+      || GET_MODE_BITSIZE (GET_MODE (dest)) > HOST_BITS_PER_WIDE_INT
+      || (c1 & nonzero_bits (other, GET_MODE (dest))) != 0)
     return x;
 
   assign = make_extraction (VOIDmode, dest, pos, NULL_RTX, len, 1, 1, 0);
@@ -7265,7 +7402,7 @@ simplify_and_const_int (x, mode, varop, constop)
   else
     {
       if (GET_CODE (XEXP (x, 1)) != CONST_INT
-         || INTVAL (XEXP (x, 1)) != constop)
+         || (unsigned HOST_WIDE_INT) INTVAL (XEXP (x, 1)) != constop)
        SUBST (XEXP (x, 1), GEN_INT (constop));
 
       SUBST (XEXP (x, 0), varop);
@@ -7624,15 +7761,23 @@ nonzero_bits (x, mode)
        {
          nonzero &= nonzero_bits (SUBREG_REG (x), mode);
 
-#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.  */
-         if (GET_MODE_SIZE (GET_MODE (x))
-             > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
-           nonzero |= (GET_MODE_MASK (GET_MODE (x))
-                       & ~ GET_MODE_MASK (GET_MODE (SUBREG_REG (x))));
+#if defined (WORD_REGISTER_OPERATIONS) && defined (LOAD_EXTEND_OP)
+         /* If this is a typical RISC machine, we only have to worry
+            about the way loads are extended.  */
+         if (LOAD_EXTEND_OP (GET_MODE (SUBREG_REG (x))) == SIGN_EXTEND
+             ? (nonzero
+                & (1L << (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) - 1)))
+             : LOAD_EXTEND_OP (GET_MODE (SUBREG_REG (x))) != ZERO_EXTEND)
 #endif
+           {
+             /* On many CISC machines, accessing an object in a wider mode
+                causes the high-order bits to become undefined.  So they are
+                not known to be zero.  */
+             if (GET_MODE_SIZE (GET_MODE (x))
+                 > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+               nonzero |= (GET_MODE_MASK (GET_MODE (x))
+                           & ~ GET_MODE_MASK (GET_MODE (SUBREG_REG (x))));
+           }
        }
       break;
 
@@ -8163,7 +8308,8 @@ merge_outer_ops (pop0, pconst0, op1, const1, mode, pcomp_p)
     op0 = NIL;
   else if (const0 == 0 && op0 == AND)
     op0 = SET;
-  else if (const0 == GET_MODE_MASK (mode) && op0 == AND)
+  else if ((unsigned HOST_WIDE_INT) const0 == GET_MODE_MASK (mode)
+          && op0 == AND)
     op0 = NIL;
 
   /* If this would be an entire word for the target, but is not for
@@ -8795,7 +8941,8 @@ simplify_shift_const (x, code, result_mode, varop, count)
              && GET_CODE (XEXP (varop, 0)) == LSHIFTRT
              && GET_CODE (XEXP (XEXP (varop, 0), 1)) == CONST_INT
              && (INTVAL (XEXP (XEXP (varop, 0), 1))
-                 >= (GET_MODE_BITSIZE (GET_MODE (XEXP (varop, 0))) - GET_MODE_BITSIZE (varop))))
+                 >= (GET_MODE_BITSIZE (GET_MODE (XEXP (varop, 0)))
+                     - GET_MODE_BITSIZE (GET_MODE (varop)))))
            {
              rtx varop_inner = XEXP (varop, 0);
 
@@ -8936,18 +9083,14 @@ simplify_shift_const (x, code, result_mode, varop, count)
    PNOTES is a pointer to a location where any REG_UNUSED notes added for
    the CLOBBERs are placed.
 
-   PADDED_SCRATCHES is set to the number of (clobber (scratch)) patterns
-   we had to add.
-
    The value is the final insn code from the pattern ultimately matched,
    or -1.  */
 
 static int
-recog_for_combine (pnewpat, insn, pnotes, padded_scratches)
+recog_for_combine (pnewpat, insn, pnotes)
      rtx *pnewpat;
      rtx insn;
      rtx *pnotes;
-     int *padded_scratches;
 {
   register rtx pat = *pnewpat;
   int insn_code_number;
@@ -8955,8 +9098,6 @@ recog_for_combine (pnewpat, insn, pnotes, padded_scratches)
   int i;
   rtx notes = 0;
 
-  *padded_scratches = 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.  */
@@ -9018,8 +9159,6 @@ recog_for_combine (pnewpat, insn, pnotes, padded_scratches)
          if (GET_CODE (XEXP (XVECEXP (newpat, 0, i), 0)) == REG
              && ! reg_dead_at_p (XEXP (XVECEXP (newpat, 0, i), 0), insn))
            return -1;
-         else if (GET_CODE (XEXP (XVECEXP (newpat, 0, i), 0)) == SCRATCH)
-           (*padded_scratches)++;
          notes = gen_rtx_EXPR_LIST (REG_UNUSED,
                                     XEXP (XVECEXP (newpat, 0, i), 0), notes);
        }
@@ -9149,7 +9288,7 @@ gen_lowpart_for_combine (mode, x)
 static rtx
 gen_rtx_combine VPROTO((enum rtx_code code, enum machine_mode mode, ...))
 {
-#ifndef __STDC__
+#ifndef ANSI_PROTOTYPES
   enum rtx_code code;
   enum machine_mode mode;
 #endif
@@ -9163,7 +9302,7 @@ gen_rtx_combine VPROTO((enum rtx_code code, enum machine_mode mode, ...))
 
   VA_START (p, mode);
 
-#ifndef __STDC__
+#ifndef ANSI_PROTOTYPES
   code = va_arg (p, enum rtx_code);
   mode = va_arg (p, enum machine_mode);
 #endif
@@ -9263,6 +9402,13 @@ gen_binary (code, mode, op0, op1)
              && GET_RTX_CLASS (GET_CODE (op1)) != 'o')))
     return gen_rtx_combine (code, mode, op1, op0);
 
+  /* If we are turning off bits already known off in OP0, we need not do
+     an AND.  */
+  else if (code == AND && GET_CODE (op1) == CONST_INT
+          && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+          && (nonzero_bits (op0, mode) & ~ INTVAL (op1)) == 0)
+    return op0;
+
   return gen_rtx_combine (code, mode, op0, op1);
 }
 
@@ -9395,10 +9541,10 @@ simplify_comparison (code, pop0, pop1)
                  > GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner_op0))))
              && (GET_MODE (SUBREG_REG (inner_op0))
                  == GET_MODE (SUBREG_REG (inner_op1)))
-             && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))
+             && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (inner_op0)))
                  <= HOST_BITS_PER_WIDE_INT)
              && (0 == ((~c0) & nonzero_bits (SUBREG_REG (inner_op0),
-                                            GET_MODE (SUBREG_REG (op0)))))
+                                            GET_MODE (SUBREG_REG (inner_op0)))))
              && (0 == ((~c1) & nonzero_bits (SUBREG_REG (inner_op1),
                                             GET_MODE (SUBREG_REG (inner_op1))))))
            {
@@ -9416,7 +9562,7 @@ simplify_comparison (code, pop0, pop1)
            for (tmode = GET_CLASS_NARROWEST_MODE
                 (GET_MODE_CLASS (GET_MODE (op0)));
                 tmode != GET_MODE (op0); tmode = GET_MODE_WIDER_MODE (tmode))
-             if (c0 == GET_MODE_MASK (tmode))
+             if ((unsigned HOST_WIDE_INT) c0 == GET_MODE_MASK (tmode))
                {
                  op0 = gen_lowpart_for_combine (tmode, inner_op0);
                  op1 = gen_lowpart_for_combine (tmode, inner_op1);
@@ -9491,7 +9637,7 @@ simplify_comparison (code, pop0, pop1)
              || code == LT || code == LTU)
          && mode_width <= HOST_BITS_PER_WIDE_INT
          && exact_log2 (const_op) >= 0
-         && nonzero_bits (op0, mode) == const_op)
+         && nonzero_bits (op0, mode) == (unsigned HOST_WIDE_INT) const_op)
        {
          code = (code == EQ || code == GE || code == GEU ? NE : EQ);
          op1 = const0_rtx, const_op = 0;
@@ -9683,12 +9829,16 @@ simplify_comparison (code, pop0, pop1)
              && (i = exact_log2 (INTVAL (XEXP (op0, 0)))) >= 0)
            {
              if (BITS_BIG_ENDIAN)
+               {
 #ifdef HAVE_extzv
-               i = (GET_MODE_BITSIZE
-                    (insn_operand_mode[(int) CODE_FOR_extzv][1]) - 1 - i);
+                 mode = insn_operand_mode[(int) CODE_FOR_extzv][1];
+                 if (mode == VOIDmode)
+                   mode = word_mode;
+                 i = (GET_MODE_BITSIZE (mode) - 1 - i);
 #else
-               i = BITS_PER_WORD - 1 - i;
+                 i = BITS_PER_WORD - 1 - i;
 #endif
+               }
 
              op0 = XEXP (op0, 2);
              op1 = GEN_INT (i);
@@ -9816,7 +9966,7 @@ simplify_comparison (code, pop0, pop1)
              && (GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 0)))
                  <= HOST_BITS_PER_WIDE_INT)
              && ((unsigned HOST_WIDE_INT) const_op
-                 < (((HOST_WIDE_INT) 1
+                 < (((unsigned HOST_WIDE_INT) 1
                      << (GET_MODE_BITSIZE (GET_MODE (XEXP (op0, 0))) - 1)))))
            {
              op0 = XEXP (op0, 0);
@@ -9840,7 +9990,7 @@ simplify_comparison (code, pop0, pop1)
              && GET_CODE (XEXP (SUBREG_REG (op0), 1)) == CONST_INT
              && INTVAL (XEXP (SUBREG_REG (op0), 1)) < 0
              && (- INTVAL (XEXP (SUBREG_REG (op0), 1))
-                 < GET_MODE_MASK (mode) / 2)
+                 < (HOST_WIDE_INT)(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)))
@@ -10044,7 +10194,7 @@ simplify_comparison (code, pop0, pop1)
              && GET_CODE (XEXP (op0, 1)) == CONST_INT
              && mode_width <= HOST_BITS_PER_WIDE_INT
              && ((INTVAL (XEXP (op0, 1)) & GET_MODE_MASK (mode))
-                 == (HOST_WIDE_INT) 1 << (mode_width - 1)))
+                 == (unsigned HOST_WIDE_INT) 1 << (mode_width - 1)))
            {
              op0 = XEXP (op0, 0);
              code = (code == EQ ? GE : LT);
@@ -10066,6 +10216,48 @@ simplify_comparison (code, pop0, pop1)
              op0 = gen_lowpart_for_combine (tmode, XEXP (op0, 0));
              continue;
            }
+
+         /* If this is (and:M1 (subreg:M2 X 0) (const_int C1)) where C1 fits
+            in both M1 and M2 and the SUBREG is either paradoxical or
+            represents the low part, permute the SUBREG and the AND and
+            try again.  */
+         if (GET_CODE (XEXP (op0, 0)) == SUBREG
+             && ((mode_width
+                  >= GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (XEXP (op0, 0)))))
+#ifdef WORD_REGISTER_OPERATIONS
+                 || subreg_lowpart_p (XEXP (op0, 0))
+#endif
+                 )
+#ifndef WORD_REGISTER_OPERATIONS
+             /* It is unsafe to commute the AND into the SUBREG if the SUBREG
+                is paradoxical and WORD_REGISTER_OPERATIONS is not defined.
+                As originally written the upper bits have a defined value
+                due to the AND operation.  However, if we commute the AND
+                inside the SUBREG then they no longer have defined values
+                and the meaning of the code has been changed.  */
+             && (GET_MODE_SIZE (GET_MODE (XEXP (op0, 0)))
+                 <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (op0, 0)))))
+#endif
+             && GET_CODE (XEXP (op0, 1)) == CONST_INT
+             && mode_width <= HOST_BITS_PER_WIDE_INT
+             && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (XEXP (op0, 0))))
+                 <= HOST_BITS_PER_WIDE_INT)
+             && (INTVAL (XEXP (op0, 1)) & ~ mask) == 0
+             && 0 == (~ GET_MODE_MASK (GET_MODE (SUBREG_REG (XEXP (op0, 0))))
+                      & INTVAL (XEXP (op0, 1)))
+             && (unsigned HOST_WIDE_INT) INTVAL (XEXP (op0, 1)) != mask
+             && ((unsigned HOST_WIDE_INT) INTVAL (XEXP (op0, 1))
+                 != GET_MODE_MASK (GET_MODE (SUBREG_REG (XEXP (op0, 0))))))
+                      
+           {
+             op0
+               = gen_lowpart_for_combine
+                 (mode,
+                  gen_binary (AND, GET_MODE (SUBREG_REG (XEXP (op0, 0))),
+                              SUBREG_REG (XEXP (op0, 0)), XEXP (op0, 1)));
+             continue;
+           }
+
          break;
 
        case ASHIFT:
@@ -11398,15 +11590,35 @@ distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1)
                  if (reg_set_p (XEXP (note, 0), PATTERN (tem)))
                    {
                      rtx set = single_set (tem);
+                     rtx inner_dest = 0;
+#ifdef HAVE_cc0
+                     rtx cc0_setter = NULL_RTX;
+#endif
+
+                     if (set != 0)
+                       for (inner_dest = SET_DEST (set);
+                            GET_CODE (inner_dest) == STRICT_LOW_PART
+                            || GET_CODE (inner_dest) == SUBREG
+                            || GET_CODE (inner_dest) == ZERO_EXTRACT;
+                            inner_dest = XEXP (inner_dest, 0))
+                         ;
 
                      /* Verify that it was the set, and not a clobber that
-                        modified the register.  */
+                        modified the register. 
+
+                        CC0 targets must be careful to maintain setter/user
+                        pairs.  If we cannot delete the setter due to side
+                        effects, mark the user with an UNUSED note instead
+                        of deleting it.  */
 
                      if (set != 0 && ! side_effects_p (SET_SRC (set))
-                         && (rtx_equal_p (XEXP (note, 0), SET_DEST (set))
-                             || (GET_CODE (SET_DEST (set)) == SUBREG
-                                 && rtx_equal_p (XEXP (note, 0),
-                                                 XEXP (SET_DEST (set), 0)))))
+                         && rtx_equal_p (XEXP (note, 0), inner_dest)
+#ifdef HAVE_cc0
+                         && (! reg_mentioned_p (cc0_rtx, SET_SRC (set))
+                             || ((cc0_setter = prev_cc0_setter (tem)) != NULL
+                                 && sets_cc0_p (PATTERN (cc0_setter)) > 0))
+#endif
+                         )
                        {
                          /* Move the notes and links of TEM elsewhere.
                             This might delete other dead insns recursively. 
@@ -11422,6 +11634,38 @@ distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1)
                          PUT_CODE (tem, NOTE);
                          NOTE_LINE_NUMBER (tem) = NOTE_INSN_DELETED;
                          NOTE_SOURCE_FILE (tem) = 0;
+
+#ifdef HAVE_cc0
+                         /* Delete the setter too.  */
+                         if (cc0_setter)
+                           {
+                             PATTERN (cc0_setter) = pc_rtx;
+
+                             distribute_notes (REG_NOTES (cc0_setter),
+                                               cc0_setter, cc0_setter,
+                                               NULL_RTX, NULL_RTX, NULL_RTX);
+                             distribute_links (LOG_LINKS (cc0_setter));
+
+                             PUT_CODE (cc0_setter, NOTE);
+                             NOTE_LINE_NUMBER (cc0_setter) = NOTE_INSN_DELETED;
+                             NOTE_SOURCE_FILE (cc0_setter) = 0;
+                           }
+#endif
+                       }
+                     /* If the register is both set and used here, put the
+                        REG_DEAD note here, but place a REG_UNUSED note
+                        here too unless there already is one.  */
+                     else if (reg_referenced_p (XEXP (note, 0),
+                                                PATTERN (tem)))
+                       {
+                         place = tem;
+
+                         if (! find_regno_note (tem, REG_UNUSED,
+                                                REGNO (XEXP (note, 0))))
+                           REG_NOTES (tem)
+                             = gen_rtx_EXPR_LIST (REG_UNUSED,
+                                                  XEXP (note, 0),
+                                                  REG_NOTES (tem));
                        }
                      else
                        {
@@ -11479,13 +11723,12 @@ distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1)
            }
 
          /* If the register is set or already dead at PLACE, we needn't do
-            anything with this note if it is still a REG_DEAD note.  
+            anything with this note if it is still a REG_DEAD note.
+            We can here if it is set at all, not if is it totally replace,
+            which is what `dead_or_set_p' checks, so also check for it being
+            set partially.  */
+
 
-            Note that we cannot use just `dead_or_set_p' here since we can
-            convert an assignment to a register into a bit-field assignment.
-            Therefore, we must also omit the note if the register is the 
-            target of a bitfield assignment.  */
-            
          if (place && REG_NOTE_KIND (note) == REG_DEAD)
            {
              int regno = REGNO (XEXP (note, 0));