OSDN Git Service

Add -mabi=n32 support.
[pf3gnuchains/gcc-fork.git] / gcc / combine.c
index 827141c..9427060 100644 (file)
@@ -1,5 +1,5 @@
 /* Optimize by combining instructions for GNU compiler.
-   Copyright (C) 1987, 88, 92, 93, 94, 1995 Free Software Foundation, Inc.
+   Copyright (C) 1987, 88, 92, 93, 94, 95, 1996 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -15,7 +15,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 
 /* This module is essentially the "combiner" phase of the U. of Arizona
@@ -120,7 +121,7 @@ static int combine_successes;
 
 static int total_attempts, total_merges, total_extras, total_successes;
 
-/* Define a defulat value for REVERSIBLE_CC_MODE.
+/* Define a default 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
@@ -139,9 +140,8 @@ static int max_uid_cuid;
 
 /* Get the cuid of an insn.  */
 
-#define INSN_CUID(INSN) (INSN_UID (INSN) > max_uid_cuid                \
-                        ? (abort(), 0)                         \
-                        : uid_cuid[INSN_UID (INSN)])
+#define INSN_CUID(INSN) \
+(INSN_UID (INSN) > max_uid_cuid ? insn_cuid (INSN) : uid_cuid[INSN_UID (INSN)])
 
 /* Maximum register number, which is the size of the tables below.  */
 
@@ -385,8 +385,8 @@ static struct undobuf undobuf;
 
 static int n_occurrences;
 
-static void init_reg_last_arrays       PROTO(());
-static void setup_incoming_promotions   PROTO(());
+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 combinable_i3pat    PROTO((rtx, rtx *, rtx, rtx, int, rtx *));
@@ -409,6 +409,7 @@ 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 int rtx_equal_for_field_assignment_p PROTO((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,
@@ -420,7 +421,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 *));
+static int recog_for_combine   PROTO((rtx *, rtx, rtx *, int *));
 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,
                                  ...));
@@ -444,6 +445,7 @@ 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));
+static int insn_cuid           PROTO((rtx));
 \f
 /* Main entry point for combiner.  F is the first insn of the function.
    NREGS is the first unused pseudo-reg number.  */
@@ -518,6 +520,10 @@ combine_instructions (f, nregs)
 
   label_tick = 1;
 
+  /* We need to initialize it here, because record_dead_and_set_regs may call
+     get_last_value.  */
+  subst_prev_insn = NULL_RTX;
+
   setup_incoming_promotions ();
 
   for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
@@ -530,6 +536,12 @@ combine_instructions (f, nregs)
        {
          note_stores (PATTERN (insn), set_nonzero_bits_and_sign_copies);
          record_dead_and_set_regs (insn);
+
+#ifdef AUTO_INC_DEC
+         for (links = REG_NOTES (insn); links; links = XEXP (links, 1))
+           if (REG_NOTE_KIND (links) == REG_INC)
+             set_nonzero_bits_and_sign_copies (XEXP (links, 0), NULL_RTX);
+#endif
        }
 
       if (GET_CODE (insn) == CODE_LABEL)
@@ -696,10 +708,8 @@ setup_incoming_promotions ()
 #endif
 }
 \f
-/* Called via note_stores.  If X is a pseudo that is used in more than
-   one basic block, is narrower that HOST_BITS_PER_WIDE_INT, and is being
-   set, record what bits are known zero.  If we are clobbering X,
-   ignore this "set" because the clobbered value won't be used. 
+/* Called via note_stores.  If X is a pseudo that is narrower than
+   HOST_BITS_PER_WIDE_INT and is being set, record what bits are known zero.
 
    If we are setting only a portion of X and we can't figure out what
    portion, assume all bits will be used since we don't know what will
@@ -718,18 +728,16 @@ set_nonzero_bits_and_sign_copies (x, set)
 
   if (GET_CODE (x) == REG
       && REGNO (x) >= FIRST_PSEUDO_REGISTER
-      && reg_n_sets[REGNO (x)] > 1
-      && reg_basic_block[REGNO (x)] < 0
       /* If this register is undefined at the start of the file, we can't
         say what its contents were.  */
       && ! (basic_block_live_at_start[0][REGNO (x) / REGSET_ELT_BITS]
            & ((REGSET_ELT_TYPE) 1 << (REGNO (x) % REGSET_ELT_BITS)))
       && GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT)
     {
-      if (GET_CODE (set) == CLOBBER)
+      if (set == 0 || GET_CODE (set) == CLOBBER)
        {
          reg_nonzero_bits[REGNO (x)] = GET_MODE_MASK (GET_MODE (x));
-         reg_sign_bit_copies[REGNO (x)] = 0;
+         reg_sign_bit_copies[REGNO (x)] = 1;
          return;
        }
 
@@ -779,7 +787,7 @@ set_nonzero_bits_and_sign_copies (x, set)
       else
        {
          reg_nonzero_bits[REGNO (x)] = GET_MODE_MASK (GET_MODE (x));
-         reg_sign_bit_copies[REGNO (x)] = 0;
+         reg_sign_bit_copies[REGNO (x)] = 1;
        }
     }
 }
@@ -1213,6 +1221,8 @@ 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;
@@ -1297,7 +1307,8 @@ try_combine (i3, i2, i1)
         The problem can also happen if the dest of I3 is a memory ref,
         if another dest in I2 is an indirect memory ref.  */
       for (i = 0; i < XVECLEN (p2, 0); i++)
-       if (GET_CODE (XVECEXP (p2, 0, i)) == SET
+       if ((GET_CODE (XVECEXP (p2, 0, i)) == SET
+            || GET_CODE (XVECEXP (p2, 0, i)) == CLOBBER)
            && reg_overlap_mentioned_p (SET_DEST (PATTERN (i3)),
                                        SET_DEST (XVECEXP (p2, 0, i))))
          break;
@@ -1681,7 +1692,8 @@ 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);
+  insn_code_number
+    = recog_for_combine (&newpat, i3, &new_i3_notes, &i3_scratches);
 
   /* 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,
@@ -1702,7 +1714,8 @@ 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);
+      insn_code_number
+       = recog_for_combine (&newpat, i3, &new_i3_notes, &i3_scratches);
     }
 
   else if (insn_code_number < 0 && GET_CODE (newpat) == PARALLEL
@@ -1715,7 +1728,8 @@ 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);
+      insn_code_number
+       = recog_for_combine (&newpat, i3, &new_i3_notes, &i3_scratches);
     }
 
   /* If we were combining three insns and the result is a simple SET
@@ -1784,25 +1798,36 @@ 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_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes,
+                                             &i2_scratches);
 
          /* If I2 or I3 has multiple SETs, we won't know how to track
             register status, so don't use these insns.  */
 
          if (i2_code_number >= 0 && i2set && i3set)
-           insn_code_number = recog_for_combine (&newi3pat, i3,
-                                                 &new_i3_notes);
-
+           insn_code_number = recog_for_combine (&newi3pat, i3, &new_i3_notes,
+                                                 &i3_scratches); 
          if (insn_code_number >= 0)
            newpat = newi3pat;
 
          /* It is possible that both insns now set the destination of I3.
             If so, we must show an extra use of it.  */
 
-         if (insn_code_number >= 0 && GET_CODE (SET_DEST (i3set)) == REG
-             && GET_CODE (SET_DEST (i2set)) == REG
-             && REGNO (SET_DEST (i3set)) == REGNO (SET_DEST (i2set)))
-           reg_n_sets[REGNO (SET_DEST (i2set))]++;
+         if (insn_code_number >= 0)
+           {
+             rtx new_i3_dest = SET_DEST (i3set);
+             rtx new_i2_dest = SET_DEST (i2set);
+
+             while (GET_CODE (new_i3_dest) == ZERO_EXTRACT
+                    || GET_CODE (new_i3_dest) == STRICT_LOW_PART
+                    || GET_CODE (new_i3_dest) == SUBREG)
+               new_i3_dest = XEXP (new_i3_dest, 0);
+
+             if (GET_CODE (new_i3_dest) == REG
+                 && GET_CODE (new_i2_dest) == REG
+                 && REGNO (new_i3_dest) == REGNO (new_i2_dest))
+               reg_n_sets[REGNO (SET_DEST (i2set))]++;
+           }
        }
 
       /* If we can split it and use I2DEST, go ahead and see if that
@@ -1863,12 +1888,14 @@ 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_code_number
+           = recog_for_combine (&newi2pat, i2, &new_i2_notes, &i2_scratches);
 
          /* 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);
+           insn_code_number
+             = recog_for_combine (&newpat, i3, &new_i3_notes, &i3_scratches);
        }
     }
 
@@ -1922,9 +1949,12 @@ 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_code_number
+       = recog_for_combine (&newi2pat, i2, &new_i2_notes, &i2_scratches);
+
       if (i2_code_number >= 0)
-       insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
+       insn_code_number
+         = recog_for_combine (&newpat, i3, &new_i3_notes, &i3_scratches);
 
       if (insn_code_number >= 0)
        {
@@ -1998,9 +2028,12 @@ try_combine (i3, i2, i1)
       newi2pat = XVECEXP (newpat, 0, 1);
       newpat = XVECEXP (newpat, 0, 0);
 
-      i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
+      i2_code_number
+       = recog_for_combine (&newi2pat, i2, &new_i2_notes, &i2_scratches);
+
       if (i2_code_number >= 0)
-       insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
+       insn_code_number
+         = recog_for_combine (&newpat, i3, &new_i3_notes, &i3_scratches);
     }
 
   /* If it still isn't recognized, fail and change things back the way they
@@ -2022,8 +2055,9 @@ 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_code_number
+       = recog_for_combine (&other_pat, undobuf.other_insn,
+                            &new_other_notes, &other_scratches);
 
       if (other_code_number < 0 && ! check_asm_operands (other_pat))
        {
@@ -2325,6 +2359,12 @@ 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.  */
@@ -2337,6 +2377,10 @@ try_combine (i3, i2, i1)
 
   combine_successes++;
 
+  /* Clear this here, so that subsequent get_last_value calls are not
+     affected.  */
+  subst_prev_insn = NULL_RTX;
+
   if (added_links_insn
       && (newi2pat == 0 || INSN_CUID (added_links_insn) < INSN_CUID (i2))
       && INSN_CUID (added_links_insn) < INSN_CUID (i3))
@@ -2364,6 +2408,10 @@ undo_all ()
 
   obfree (undobuf.storage);
   undobuf.num_undo = 0;
+
+  /* Clear this here, so that subsequent get_last_value calls are not
+     affected.  */
+  subst_prev_insn = NULL_RTX;
 }
 \f
 /* Find the innermost point within the rtx at LOC, possibly LOC itself,
@@ -2414,7 +2462,7 @@ find_split_point (loc, insn)
       /* If we have a PLUS whose second operand is a constant and the
         address is not valid, perhaps will can split it up using
         the machine-specific way to split large constants.  We use
-        the first psuedo-reg (one of the virtual regs) as a placeholder;
+        the first pseudo-reg (one of the virtual regs) as a placeholder;
         it will not remain in the result.  */
       if (GET_CODE (XEXP (x, 0)) == PLUS
          && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
@@ -2498,6 +2546,11 @@ find_split_point (loc, insn)
       if (split && split != &SET_SRC (x))
        return split;
 
+      /* See if we can split SET_DEST as it stands.  */
+      split = find_split_point (&SET_DEST (x), insn);
+      if (split && split != &SET_DEST (x))
+       return split;
+
       /* See if this is a bitfield assignment with everything constant.  If
         so, this is an IOR of an AND, so split it into that.  */
       if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT
@@ -2564,11 +2617,14 @@ find_split_point (loc, insn)
              && XEXP (*split, 0) == SET_DEST (x)
              && XEXP (*split, 1) == const0_rtx)
            {
-             SUBST (SET_SRC (x),
-                    make_extraction (GET_MODE (SET_DEST (x)),
-                                     XEXP (SET_SRC (x), 0),
-                                     pos, NULL_RTX, 1, 1, 0, 0));
-             return find_split_point (loc, insn);
+             rtx extraction = make_extraction (GET_MODE (SET_DEST (x)),
+                                               XEXP (SET_SRC (x), 0),
+                                               pos, NULL_RTX, 1, 1, 0, 0);
+             if (extraction != 0)
+               {
+                 SUBST (SET_SRC (x), extraction);
+                 return find_split_point (loc, insn);
+               }
            }
          break;
 
@@ -3024,6 +3080,9 @@ simplify_rtx (x, op0_mode, last, in_dest)
          rtx cop1 = const0_rtx;
          enum rtx_code cond_code = simplify_comparison (NE, &cond, &cop1);
 
+         if (cond_code == NE && GET_RTX_CLASS (GET_CODE (cond)) == '<')
+           return x;
+
          /* 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);
@@ -3255,7 +3314,7 @@ simplify_rtx (x, op0_mode, last, in_dest)
         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)
          && (! WORDS_BIG_ENDIAN
              || GET_MODE_BITSIZE (op0_mode) <= BITS_PER_WORD))
        return gen_lowpart_for_combine (mode, SUBREG_REG (x));
@@ -3982,6 +4041,10 @@ simplify_if_then_else (x)
       SUBST (XEXP (x, 2), true);
 
       temp = true, true = false, false = temp, cond = XEXP (x, 0);
+
+      /* It is possible that the conditional has been simplified out. */
+      true_code = GET_CODE (cond);
+      comparison_p = GET_RTX_CLASS (true_code) == '<';
     }
 
   /* If the two arms are identical, we don't need the comparison.  */
@@ -3989,6 +4052,16 @@ simplify_if_then_else (x)
   if (rtx_equal_p (true, false) && ! side_effects_p (cond))
     return true;
 
+  /* Convert a == b ? b : a to "a".  */
+  if (true_code == EQ && ! side_effects_p (cond)
+      && rtx_equal_p (XEXP (cond, 0), false)
+      && rtx_equal_p (XEXP (cond, 1), true))
+    return false;
+  else if (true_code == NE && ! side_effects_p (cond)
+          && rtx_equal_p (XEXP (cond, 0), true)
+          && rtx_equal_p (XEXP (cond, 1), false))
+    return true;
+
   /* Look for cases where we have (abs x) or (neg (abs X)).  */
 
   if (GET_MODE_CLASS (mode) == MODE_INT
@@ -4274,8 +4347,9 @@ 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) < 0
+             if ((recog_for_combine (&pat, other_insn, &note, &scratches) < 0
                   && ! check_asm_operands (pat)))
                {
                  PUT_CODE (*cc_use, old_code);
@@ -4883,7 +4957,7 @@ expand_field_assignment (x)
        {
          inner = SUBREG_REG (XEXP (SET_DEST (x), 0));
          len = GET_MODE_BITSIZE (GET_MODE (XEXP (SET_DEST (x), 0)));
-         pos = const0_rtx;
+         pos = GEN_INT (BITS_PER_WORD * SUBREG_WORD (XEXP (SET_DEST (x), 0)));
        }
       else if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT
               && GET_CODE (XEXP (SET_DEST (x), 1)) == CONST_INT)
@@ -4990,7 +5064,10 @@ expand_field_assignment (x)
    IN_COMPARE is non-zero if we are in a COMPARE.  This means that a
    ZERO_EXTRACT should be built even for bits starting at bit 0.
 
-   MODE is the desired mode of the result (if IN_DEST == 0).  */
+   MODE is the desired mode of the result (if IN_DEST == 0).
+
+   The result is an RTX for the extraction or NULL_RTX if the target
+   can't handle it.  */
 
 static rtx
 make_extraction (mode, inner, pos, pos_rtx, len,
@@ -5008,7 +5085,8 @@ make_extraction (mode, inner, pos, pos_rtx, len,
      ignore the POS lowest bits, etc.  */
   enum machine_mode is_mode = GET_MODE (inner);
   enum machine_mode inner_mode;
-  enum machine_mode wanted_mem_mode = byte_mode;
+  enum machine_mode wanted_inner_mode = byte_mode;
+  enum machine_mode wanted_inner_reg_mode = word_mode;
   enum machine_mode pos_mode = word_mode;
   enum machine_mode extraction_mode = word_mode;
   enum machine_mode tmode = mode_for_size (len, MODE_INT, 1);
@@ -5055,7 +5133,8 @@ make_extraction (mode, inner, pos, pos_rtx, len,
 
   if (tmode != BLKmode
       && ! (spans_byte && inner_mode != tmode)
-      && ((pos_rtx == 0 && pos == 0 && GET_CODE (inner) != MEM
+      && ((pos_rtx == 0 && (pos % BITS_PER_WORD) == 0
+          && GET_CODE (inner) != MEM
           && (! in_dest
               || (GET_CODE (inner) == REG
                   && (movstrict_optab->handlers[(int) tmode].insn_code
@@ -5075,8 +5154,8 @@ make_extraction (mode, inner, pos, pos_rtx, len,
         field.  If the original and current mode are the same, we need not
         adjust the offset.  Otherwise, we do if bytes big endian.  
 
-        If INNER is not a MEM, get a piece consisting of the just the field
-        of interest (in this case POS must be 0).  */
+        If INNER is not a MEM, get a piece consisting of just the field
+        of interest (in this case POS % BITS_PER_WORD must be 0).  */
 
       if (GET_CODE (inner) == MEM)
        {
@@ -5100,10 +5179,11 @@ make_extraction (mode, inner, pos, pos_rtx, len,
            new = gen_rtx (SUBREG, tmode, inner,
                           (WORDS_BIG_ENDIAN
                            && GET_MODE_SIZE (inner_mode) > UNITS_PER_WORD
-                           ? ((GET_MODE_SIZE (inner_mode)
-                               - GET_MODE_SIZE (tmode))
-                              / UNITS_PER_WORD)
-                           : 0));
+                           ? (((GET_MODE_SIZE (inner_mode)
+                                - GET_MODE_SIZE (tmode))
+                               / UNITS_PER_WORD)
+                              - pos / BITS_PER_WORD)
+                           : pos / BITS_PER_WORD));
          else
            new = inner;
        }
@@ -5148,12 +5228,12 @@ make_extraction (mode, inner, pos, pos_rtx, len,
          || (pos_rtx != 0 && len != 1)))
     return 0;
 
-  /* Get the mode to use should INNER be a MEM, the mode for the position,
+  /* Get the mode to use should INNER not be a MEM, the mode for the position,
      and the mode for the result.  */
 #ifdef HAVE_insv
   if (in_dest)
     {
-      wanted_mem_mode = insn_operand_mode[(int) CODE_FOR_insv][0];
+      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];
     }
@@ -5162,7 +5242,7 @@ make_extraction (mode, inner, pos, pos_rtx, len,
 #ifdef HAVE_extzv
   if (! in_dest && unsignedp)
     {
-      wanted_mem_mode = insn_operand_mode[(int) CODE_FOR_extzv][1];
+      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];
     }
@@ -5171,7 +5251,7 @@ make_extraction (mode, inner, pos, pos_rtx, len,
 #ifdef HAVE_extv
   if (! in_dest && ! unsignedp)
     {
-      wanted_mem_mode = insn_operand_mode[(int) CODE_FOR_extv][1];
+      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];
     }
@@ -5187,40 +5267,48 @@ make_extraction (mode, inner, pos, pos_rtx, len,
       && GET_MODE_SIZE (pos_mode) < GET_MODE_SIZE (GET_MODE (pos_rtx)))
     pos_mode = GET_MODE (pos_rtx);
 
-  /* If this is not from memory or we have to change the mode of memory and
-     cannot, the desired mode is EXTRACTION_MODE.  */
-  if (GET_CODE (inner) != MEM
-      || (inner_mode != wanted_mem_mode
-         && (mode_dependent_address_p (XEXP (inner, 0))
-             || MEM_VOLATILE_P (inner))))
-    wanted_mem_mode = extraction_mode;
+  /* If this is not from memory, the desired mode is wanted_inner_reg_mode;
+     if we have to change the mode of memory and cannot, the desired mode is
+     EXTRACTION_MODE.  */
+  if (GET_CODE (inner) != MEM)
+    wanted_inner_mode = wanted_inner_reg_mode;
+  else if (inner_mode != wanted_inner_mode
+          && (mode_dependent_address_p (XEXP (inner, 0))
+              || MEM_VOLATILE_P (inner)))
+    wanted_inner_mode = extraction_mode;
 
   orig_pos = pos;
 
   if (BITS_BIG_ENDIAN)
     {
-      /* If position is constant, compute new position.  Otherwise,
-        build subtraction.  */
+      /* POS is passed as if BITS_BIG_ENDIAN == 0, so we need to convert it to
+        BITS_BIG_ENDIAN style.  If position is constant, compute new
+        position.  Otherwise, build subtraction.
+        Note that POS is relative to the mode of the original argument.
+        If it's a MEM we need to recompute POS relative to that.
+        However, if we're extracting from (or inserting into) a register,
+        we want to recompute POS relative to wanted_inner_mode.  */
+      int width = (GET_CODE (inner) == MEM
+                  ? GET_MODE_BITSIZE (is_mode)
+                  : GET_MODE_BITSIZE (wanted_inner_mode));
+
       if (pos_rtx == 0)
-       pos = (MAX (GET_MODE_BITSIZE (is_mode),
-                   GET_MODE_BITSIZE (wanted_mem_mode))
-              - len - pos);
+       pos = width - len - pos;
       else
        pos_rtx
          = gen_rtx_combine (MINUS, GET_MODE (pos_rtx),
-                            GEN_INT (MAX (GET_MODE_BITSIZE (is_mode),
-                                          GET_MODE_BITSIZE (wanted_mem_mode))
-                                     - len),
-                            pos_rtx);
+                            GEN_INT (width - len), pos_rtx);
+      /* POS may be less than 0 now, but we check for that below.
+        Note that it can only be less than 0 if GET_CODE (inner) != MEM.  */
     }
 
   /* If INNER has a wider mode, make it smaller.  If this is a constant
      extract, try to adjust the byte to point to the byte containing
      the value.  */
-  if (wanted_mem_mode != VOIDmode
-      && GET_MODE_SIZE (wanted_mem_mode) < GET_MODE_SIZE (is_mode)
+  if (wanted_inner_mode != VOIDmode
+      && GET_MODE_SIZE (wanted_inner_mode) < GET_MODE_SIZE (is_mode)
       && ((GET_CODE (inner) == MEM
-          && (inner_mode == wanted_mem_mode
+          && (inner_mode == wanted_inner_mode
               || (! mode_dependent_address_p (XEXP (inner, 0))
                   && ! MEM_VOLATILE_P (inner))))))
     {
@@ -5241,18 +5329,18 @@ make_extraction (mode, inner, pos, pos_rtx, len,
       if (pos_rtx == 0)
        {
          offset += pos / BITS_PER_UNIT;
-         pos %= GET_MODE_BITSIZE (wanted_mem_mode);
+         pos %= GET_MODE_BITSIZE (wanted_inner_mode);
        }
 
       if (BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN
          && ! spans_byte
-         && is_mode != wanted_mem_mode)
+         && is_mode != wanted_inner_mode)
        offset = (GET_MODE_SIZE (is_mode)
-                 - GET_MODE_SIZE (wanted_mem_mode) - offset);
+                 - GET_MODE_SIZE (wanted_inner_mode) - offset);
 
-      if (offset != 0 || inner_mode != wanted_mem_mode)
+      if (offset != 0 || inner_mode != wanted_inner_mode)
        {
-         rtx newmem = gen_rtx (MEM, wanted_mem_mode,
+         rtx newmem = gen_rtx (MEM, wanted_inner_mode,
                                plus_constant (XEXP (inner, 0), offset));
          RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (inner);
          MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (inner);
@@ -5261,13 +5349,23 @@ make_extraction (mode, inner, pos, pos_rtx, len,
        }
     }
 
-  /* If INNER is not memory, we can always get it into the proper mode. */
+  /* If INNER is not memory, we can always get it into the proper mode.  If we
+     are changing its mode, POS must be a constant and smaller than the size
+     of the new mode.  */
   else if (GET_CODE (inner) != MEM)
-    inner = force_to_mode (inner, extraction_mode,
-                          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);
+    {
+      if (GET_MODE (inner) != wanted_inner_mode
+         && (pos_rtx != 0
+             || orig_pos + len > GET_MODE_BITSIZE (wanted_inner_mode)))
+       return 0;
+
+      inner = force_to_mode (inner, wanted_inner_mode,
+                            pos_rtx
+                            || len + orig_pos >= HOST_BITS_PER_WIDE_INT
+                            ? GET_MODE_MASK (wanted_inner_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.  */
@@ -5867,13 +5965,14 @@ force_to_mode (x, mode, mask, reg, just_select)
               + 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))
+             & ~ 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));
+         x = gen_binary (LSHIFTRT, GET_MODE (x), temp,
+                         XEXP (XEXP (x, 0), 1));
          return force_to_mode (x, mode, mask, reg, next_select);
        }
 
@@ -5981,8 +6080,9 @@ force_to_mode (x, mode, mask, reg, just_select)
     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)))
+      if (GET_MODE_BITSIZE (GET_MODE (x)) <= HOST_BITS_PER_WIDE_INT
+         && (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
@@ -5997,8 +6097,30 @@ force_to_mode (x, mode, mask, reg, just_select)
        {
          int i = -1;
 
-         nonzero = GET_MODE_MASK (GET_MODE (x));
-         nonzero >>= INTVAL (XEXP (x, 1));
+         /* If the considered data is wider then HOST_WIDE_INT, we can't
+            represent a mask for all its bits in a single scalar.
+            But we only care about the lower bits, so calculate these.  */
+
+         if (GET_MODE_BITSIZE (GET_MODE (x)) > HOST_BITS_PER_WIDE_INT)
+           {
+             nonzero = ~(HOST_WIDE_INT)0;
+
+             /* GET_MODE_BITSIZE (GET_MODE (x)) - INTVAL (XEXP (x, 1))
+                is the number of bits a full-width mask would have set.
+                We need only shift if these are fewer than nonzero can
+                hold.  If not, we must keep all bits set in nonzero.  */
+
+             if (GET_MODE_BITSIZE (GET_MODE (x)) - INTVAL (XEXP (x, 1))
+                 < HOST_BITS_PER_WIDE_INT)
+               nonzero >>= INTVAL (XEXP (x, 1))
+                           + HOST_BITS_PER_WIDE_INT
+                           - GET_MODE_BITSIZE (GET_MODE (x)) ;
+           }
+         else
+           {
+             nonzero = GET_MODE_MASK (GET_MODE (x));
+             nonzero >>= INTVAL (XEXP (x, 1));
+           }
 
          if ((mask & ~ nonzero) == 0
              || (i = exact_log2 (mask)) >= 0)
@@ -6085,6 +6207,10 @@ force_to_mode (x, mode, mask, reg, just_select)
          return force_to_mode (x, mode, mask, reg, next_select);
        }
 
+      /* (and (not FOO) CONST) is (not (or FOO (not CONST))), so we must
+        use the full mask inside the NOT.  */
+      mask = fuller_mask;
+
     unop:
       op0 = gen_lowpart_for_combine (op_mode,
                                     force_to_mode (XEXP (x, 0), mode, mask,
@@ -6151,7 +6277,7 @@ if_then_else_cond (x, ptrue, pfalse)
     }
 
   /* If this is a COMPARE, do nothing, since the IF_THEN_ELSE we would
-     make can't possibly match and would supress other optimizations.  */
+     make can't possibly match and would suppress other optimizations.  */
   else if (code == COMPARE)
     ;
 
@@ -6385,6 +6511,46 @@ known_cond (x, cond, reg, val)
   return x;
 }
 \f
+/* See if X and Y are equal for the purposes of seeing if we can rewrite an
+   assignment as a field assignment.  */
+
+static int
+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;
+
+  if (x == 0 || y == 0 || GET_MODE (x) != GET_MODE (y))
+    return 0;
+
+  /* Check for a paradoxical SUBREG of a MEM compared with the MEM.
+     Note that all SUBREGs of MEM are paradoxical; otherwise they
+     would have been rewritten.  */
+  if (GET_CODE (x) == MEM && GET_CODE (y) == SUBREG
+      && GET_CODE (SUBREG_REG (y)) == MEM
+      && rtx_equal_p (SUBREG_REG (y),
+                     gen_lowpart_for_combine (GET_MODE (SUBREG_REG (y)), x)))
+    return 1;
+
+  if (GET_CODE (y) == MEM && GET_CODE (x) == SUBREG
+      && GET_CODE (SUBREG_REG (x)) == MEM
+      && rtx_equal_p (SUBREG_REG (x),
+                     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 && rtx_equal_for_field_assignment_p (last_x, y))
+         || (last_y != 0 && rtx_equal_for_field_assignment_p (x, last_y))
+         || (last_x != 0 && last_y != 0
+             && rtx_equal_for_field_assignment_p (last_x, last_y)));
+}
+\f
 /* See if X, a SET operation, can be rewritten as a bit-field assignment.
    Return that assignment if so.
 
@@ -6397,6 +6563,7 @@ make_field_assignment (x)
   rtx dest = SET_DEST (x);
   rtx src = SET_SRC (x);
   rtx assign;
+  rtx rhs, lhs;
   HOST_WIDE_INT c1;
   int pos, len;
   rtx other;
@@ -6410,13 +6577,13 @@ make_field_assignment (x)
   if (GET_CODE (src) == AND && GET_CODE (XEXP (src, 0)) == ROTATE
       && GET_CODE (XEXP (XEXP (src, 0), 0)) == CONST_INT
       && INTVAL (XEXP (XEXP (src, 0), 0)) == -2
-      && (rtx_equal_p (dest, XEXP (src, 1))
-         || rtx_equal_p (dest, get_last_value (XEXP (src, 1)))
-         || rtx_equal_p (get_last_value (dest), XEXP (src, 1))))
+      && rtx_equal_for_field_assignment_p (dest, XEXP (src, 1)))
     {
       assign = make_extraction (VOIDmode, dest, 0, XEXP (XEXP (src, 0), 1),
                                1, 1, 1, 0);
-      return gen_rtx (SET, VOIDmode, assign, const0_rtx);
+      if (assign != 0)
+       return gen_rtx (SET, VOIDmode, assign, const0_rtx);
+      return x;
     }
 
   else if (GET_CODE (src) == AND && GET_CODE (XEXP (src, 0)) == SUBREG
@@ -6425,60 +6592,63 @@ make_field_assignment (x)
               < GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (src, 0)))))
           && GET_CODE (SUBREG_REG (XEXP (src, 0))) == ROTATE
           && INTVAL (XEXP (SUBREG_REG (XEXP (src, 0)), 0)) == -2
-          && (rtx_equal_p (dest, XEXP (src, 1))
-              || rtx_equal_p (dest, get_last_value (XEXP (src, 1)))
-              || rtx_equal_p (get_last_value (dest), XEXP (src, 1))))
+          && rtx_equal_for_field_assignment_p (dest, XEXP (src, 1)))
     {
       assign = make_extraction (VOIDmode, dest, 0,
                                XEXP (SUBREG_REG (XEXP (src, 0)), 1),
                                1, 1, 1, 0);
-      return gen_rtx (SET, VOIDmode, assign, const0_rtx);
+      if (assign != 0)
+       return gen_rtx (SET, VOIDmode, assign, const0_rtx);
+      return x;
     }
 
-  /* If SRC is (ior (ashift (const_int 1) POS DEST)), this is a set of a
+  /* If SRC is (ior (ashift (const_int 1) POS) DEST), this is a set of a
      one-bit field.  */
   else if (GET_CODE (src) == IOR && GET_CODE (XEXP (src, 0)) == ASHIFT
           && XEXP (XEXP (src, 0), 0) == const1_rtx
-          && (rtx_equal_p (dest, XEXP (src, 1))
-              || rtx_equal_p (dest, get_last_value (XEXP (src, 1)))
-              || rtx_equal_p (get_last_value (dest), XEXP (src, 1))))
+          && rtx_equal_for_field_assignment_p (dest, XEXP (src, 1)))
     {
       assign = make_extraction (VOIDmode, dest, 0, XEXP (XEXP (src, 0), 1),
                                1, 1, 1, 0);
-      return gen_rtx (SET, VOIDmode, assign, const1_rtx);
+      if (assign != 0)
+       return gen_rtx (SET, VOIDmode, assign, const1_rtx);
+      return x;
     }
 
   /* The other case we handle is assignments into a constant-position
-     field.  They look like (ior (and DEST C1) OTHER).  If C1 represents
+     field.  They look like (ior/xor (and DEST C1) OTHER).  If C1 represents
      a mask that has all one bits except for a group of zero bits and
      OTHER is known to have zeros where C1 has ones, this is such an
      assignment.  Compute the position and length from C1.  Shift OTHER
      to the appropriate position, force it to the required mode, and
      make the extraction.  Check for the AND in both operands.  */
 
-  if (GET_CODE (src) == IOR && GET_CODE (XEXP (src, 0)) == AND
-      && GET_CODE (XEXP (XEXP (src, 0), 1)) == CONST_INT
-      && (rtx_equal_p (XEXP (XEXP (src, 0), 0), dest)
-         || rtx_equal_p (XEXP (XEXP (src, 0), 0), get_last_value (dest))
-         || rtx_equal_p (get_last_value (XEXP (XEXP (src, 0), 1)), dest)))
-    c1 = INTVAL (XEXP (XEXP (src, 0), 1)), other = XEXP (src, 1);
-  else if (GET_CODE (src) == IOR && GET_CODE (XEXP (src, 1)) == AND
-          && GET_CODE (XEXP (XEXP (src, 1), 1)) == CONST_INT
-          && (rtx_equal_p (XEXP (XEXP (src, 1), 0), dest)
-              || rtx_equal_p (XEXP (XEXP (src, 1), 0), get_last_value (dest))
-              || rtx_equal_p (get_last_value (XEXP (XEXP (src, 1), 0)),
-                              dest)))
-    c1 = INTVAL (XEXP (XEXP (src, 1), 1)), other = XEXP (src, 0);
+  if (GET_CODE (src) != IOR && GET_CODE (src) != XOR)
+    return x;
+
+  rhs = expand_compound_operation (XEXP (src, 0));
+  lhs = expand_compound_operation (XEXP (src, 1));
+
+  if (GET_CODE (rhs) == AND
+      && GET_CODE (XEXP (rhs, 1)) == CONST_INT
+      && rtx_equal_for_field_assignment_p (XEXP (rhs, 0), dest))
+    c1 = INTVAL (XEXP (rhs, 1)), other = lhs;
+  else if (GET_CODE (lhs) == AND
+          && GET_CODE (XEXP (lhs, 1)) == CONST_INT
+          && rtx_equal_for_field_assignment_p (XEXP (lhs, 0), dest))
+    c1 = INTVAL (XEXP (lhs, 1)), other = rhs;
   else
     return x;
 
-  pos = get_pos_from_mask (c1 ^ GET_MODE_MASK (GET_MODE (dest)), &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))
     return x;
 
   assign = make_extraction (VOIDmode, dest, pos, NULL_RTX, len, 1, 1, 0);
+  if (assign == 0)
+    return x;
 
   /* The mode to use for the source is the mode of the assignment, or of
      what is inside a possible STRICT_LOW_PART.  */
@@ -6790,32 +6960,44 @@ nonzero_bits (x, mode)
   switch (code)
     {
     case REG:
+#ifdef POINTERS_EXTEND_UNSIGNED
+      /* If pointers extend unsigned and this is a pointer in Pmode, say that
+        all the bits above ptr_mode are known to be zero.  */
+      if (POINTERS_EXTEND_UNSIGNED && GET_MODE (x) == Pmode
+         && REGNO_POINTER_FLAG (REGNO (x)))
+       nonzero &= GET_MODE_MASK (ptr_mode);
+#endif
+
 #ifdef STACK_BOUNDARY
       /* If this is the stack pointer, we may know something about its
         alignment.  If PUSH_ROUNDING is defined, it is possible for the
         stack to be momentarily aligned only to that amount, so we pick
         the least alignment.  */
 
-      if (x == stack_pointer_rtx)
+      /* We can't check for arg_pointer_rtx here, because it is not
+        guaranteed to have as much alignment as the stack pointer.
+        In particular, in the Irix6 n64 ABI, the stack has 128 bit
+        alignment but the argument pointer has only 64 bit alignment.  */
+
+      if (x == stack_pointer_rtx || x == frame_pointer_rtx
+         || x == hard_frame_pointer_rtx
+         || (REGNO (x) >= FIRST_VIRTUAL_REGISTER
+             && REGNO (x) <= LAST_VIRTUAL_REGISTER))
        {
          int sp_alignment = STACK_BOUNDARY / BITS_PER_UNIT;
 
 #ifdef PUSH_ROUNDING
-         sp_alignment = MIN (PUSH_ROUNDING (1), sp_alignment);
+         if (REGNO (x) == STACK_POINTER_REGNUM)
+           sp_alignment = MIN (PUSH_ROUNDING (1), sp_alignment);
 #endif
 
-         nonzero &= ~ (sp_alignment - 1);
+         /* We must return here, otherwise we may get a worse result from
+            one of the choices below.  There is nothing useful below as
+            far as the stack pointer is concerned.  */
+         return nonzero &= ~ (sp_alignment - 1);
        }
 #endif
 
-#ifdef POINTERS_EXTEND_UNSIGNED
-      /* If pointers extend unsigned and this is a pointer in Pmode, say that
-        all the bits above ptr_mode are known to be zero.  */
-      if (POINTERS_EXTEND_UNSIGNED && GET_MODE (x) == Pmode
-         && REGNO_POINTER_FLAG (REGNO (x)))
-       nonzero &= GET_MODE_MASK (ptr_mode);
-#endif
-
       /* If X is a register whose nonzero bits value is current, use it.
         Otherwise, if X is a register whose value we can find, use that
         value.  Otherwise, use the previously-computed global nonzero bits
@@ -7319,8 +7501,8 @@ num_sign_bit_copies (x, mode)
          && bitwidth <= HOST_BITS_PER_WIDE_INT
          && ((nonzero_bits (XEXP (x, 0), mode)
               & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0)
-         && (nonzero_bits (XEXP (x, 1), mode)
-             & ((HOST_WIDE_INT) 1 << (bitwidth - 1)) != 0))
+         && ((nonzero_bits (XEXP (x, 1), mode)
+             & ((HOST_WIDE_INT) 1 << (bitwidth - 1))) != 0))
        result--;
 
       return MAX (1, result);
@@ -7624,15 +7806,17 @@ simplify_shift_const (x, code, result_mode, varop, count)
       if (complement_p)
        break;
 
-      /* Convert ROTATETRT to ROTATE.  */
+      /* Convert ROTATERT to ROTATE.  */
       if (code == ROTATERT)
        code = ROTATE, count = GET_MODE_BITSIZE (result_mode) - count;
 
       /* 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
-        mode encountered. */
-      shift_mode = (code == ASHIFTRT || code == ROTATE ? result_mode : mode);
+        shift is a right shift or a ROTATE, we must always do it in the mode
+        it was originally done in.  Otherwise, we can do it in MODE, the
+        widest mode encountered. */
+      shift_mode
+       = (code == ASHIFTRT || code == LSHIFTRT || code == ROTATE
+          ? result_mode : mode);
 
       /* Handle cases where the count is greater than the size of the mode
         minus 1.  For ASHIFT, use the size minus one as the count (this can
@@ -7884,7 +8068,7 @@ simplify_shift_const (x, code, result_mode, varop, count)
                 ASHIFTRT.
 
                 If the mode of this shift is not the mode of the outer shift,
-                we can't do this if either shift is ASHIFTRT or ROTATE.
+                we can't do this if either shift is a right shift or ROTATE.
 
                 Finally, we can't do any of these if the mode is too wide
                 unless the codes are the same.
@@ -7895,7 +8079,8 @@ simplify_shift_const (x, code, result_mode, varop, count)
              if (code == first_code)
                {
                  if (GET_MODE (varop) != result_mode
-                     && (code == ASHIFTRT || code == ROTATE))
+                     && (code == ASHIFTRT || code == LSHIFTRT
+                         || code == ROTATE))
                    break;
 
                  count += first_count;
@@ -7907,7 +8092,8 @@ simplify_shift_const (x, code, result_mode, varop, count)
                  || (code == ROTATE && first_code == ASHIFTRT)
                  || GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT
                  || (GET_MODE (varop) != result_mode
-                     && (first_code == ASHIFTRT || first_code == ROTATE
+                     && (first_code == ASHIFTRT || first_code == LSHIFTRT
+                         || first_code == ROTATE
                          || code == ROTATE)))
                break;
 
@@ -8173,11 +8359,13 @@ simplify_shift_const (x, code, result_mode, varop, count)
     }
 
   /* We need to determine what mode to 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 mode encountered.
-     The code we care about is that of the shift that will actually be done,
-     not the shift that was originally requested.  */
-  shift_mode = (code == ASHIFTRT || code == ROTATE ? result_mode : mode);
+     a right shift or ROTATE, we must always do it in the mode it was
+     originally done in.  Otherwise, we can do it in MODE, the widest mode
+     encountered.  The code we care about is that of the shift that will
+     actually be done, not the shift that was originally requested.  */
+  shift_mode
+    = (code == ASHIFTRT || code == LSHIFTRT || code == ROTATE
+       ? result_mode : mode);
 
   /* We have now finished analyzing the shift.  The result should be
      a shift of type CODE with SHIFT_MODE shifting VAROP COUNT places.  If
@@ -8289,14 +8477,18 @@ 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)
+recog_for_combine (pnewpat, insn, pnotes, padded_scratches)
      rtx *pnewpat;
      rtx insn;
      rtx *pnotes;
+     int *padded_scratches;
 {
   register rtx pat = *pnewpat;
   int insn_code_number;
@@ -8304,6 +8496,8 @@ recog_for_combine (pnewpat, insn, pnotes)
   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.  */
@@ -8365,6 +8559,8 @@ recog_for_combine (pnewpat, insn, pnotes)
          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);
        }
@@ -8741,10 +8937,10 @@ simplify_comparison (code, pop0, pop1)
                  == GET_MODE (SUBREG_REG (inner_op1)))
              && (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)))
                  <= HOST_BITS_PER_WIDE_INT)
-             && (0 == (~c0) & nonzero_bits (SUBREG_REG (inner_op0),
-                                            GET_MODE (SUBREG_REG (op0))))
-             && (0 == (~c1) & nonzero_bits (SUBREG_REG (inner_op1),
-                                            GET_MODE (SUBREG_REG (inner_op1)))))
+             && (0 == ((~c0) & nonzero_bits (SUBREG_REG (inner_op0),
+                                            GET_MODE (SUBREG_REG (op0)))))
+             && (0 == ((~c1) & nonzero_bits (SUBREG_REG (inner_op1),
+                                            GET_MODE (SUBREG_REG (inner_op1))))))
            {
              op0 = SUBREG_REG (inner_op0);
              op1 = SUBREG_REG (inner_op1);
@@ -9011,24 +9207,19 @@ simplify_comparison (code, pop0, pop1)
          /* If we are extracting a single bit from a variable position in
             a constant that has only a single bit set and are comparing it
             with zero, we can convert this into an equality comparison 
-            between the position and the location of the single bit.  We can't
-            do this if bit endian and we don't have an extzv since we then
-            can't know what mode to use for the endianness adjustment.  */
+            between the position and the location of the single bit.  */
 
          if (GET_CODE (XEXP (op0, 0)) == CONST_INT
              && XEXP (op0, 1) == const1_rtx
              && equality_comparison_p && const_op == 0
-             && (i = exact_log2 (INTVAL (XEXP (op0, 0)))) >= 0
-             && (! BITS_BIG_ENDIAN
-#ifdef HAVE_extzv
-                 || HAVE_extzv
-#endif
-                 ))
+             && (i = exact_log2 (INTVAL (XEXP (op0, 0)))) >= 0)
            {
-#ifdef HAVE_extzv
              if (BITS_BIG_ENDIAN)
+#ifdef HAVE_extzv
                i = (GET_MODE_BITSIZE
                     (insn_operand_mode[(int) CODE_FOR_extzv][1]) - 1 - i);
+#else
+               i = BITS_PER_WORD - 1 - i;
 #endif
 
              op0 = XEXP (op0, 2);
@@ -9167,7 +9358,7 @@ simplify_comparison (code, pop0, pop1)
 
        case SUBREG:
          /* Check for the case where we are comparing A - C1 with C2,
-            both constants are smaller than 1/2 the maxium positive
+            both constants are smaller than 1/2 the maximum positive
             value in MODE, and the comparison is equality or unsigned.
             In that case, if A is either zero-extended to MODE or has
             sufficient sign bits so that the high-order bit in MODE
@@ -9965,10 +10156,15 @@ get_last_value (x)
      This does not work if there exists an instruction which is temporarily
      not on the insn chain.  */
 
-  if (INSN_CUID (reg_last_set[regno]) >= subst_low_cuid && ! subst_prev_insn)
+  if (INSN_CUID (reg_last_set[regno]) >= subst_low_cuid)
     {
       rtx insn, set;
 
+      /* We can not do anything useful in this case, because there is
+        an instruction which is not on the insn chain.  */
+      if (subst_prev_insn)
+       return 0;
+
       /* Skip over USE insns.  They are not useful here, and they may have
         been made by combine, in which case they do not have a INSN_CUID
         value.  We can't use prev_real_insn, because that would incorrectly
@@ -9990,7 +10186,7 @@ get_last_value (x)
          value = SET_SRC (set);
 
          /* Make sure that VALUE doesn't reference X.  Replace any
-            expliit references with a CLOBBER.  If there are any remaining
+            explicit references with a CLOBBER.  If there are any remaining
             references (rare), don't use the value.  */
 
          if (reg_mentioned_p (x, value))
@@ -10078,7 +10274,7 @@ static int reg_dead_flag;
 
 /* Function called via note_stores from reg_dead_at_p.
 
-   If DEST is within [reg_dead_rengno, reg_dead_endregno), set 
+   If DEST is within [reg_dead_regno, reg_dead_endregno), set 
    reg_dead_flag to 1 if X is a CLOBBER and to -1 it is a SET.  */
 
 static void
@@ -10133,7 +10329,7 @@ reg_dead_at_p (reg, insn)
 
   /* Scan backwards until we find a REG_DEAD note, SET, CLOBBER, label, or
      beginning of function.  */
-  for (; insn && GET_CODE (insn) != CODE_LABEL;
+  for (; insn && GET_CODE (insn) != CODE_LABEL && GET_CODE (insn) != BARRIER;
        insn = prev_nonnote_insn (insn))
     {
       note_stores (PATTERN (insn), reg_dead_at_p_1);
@@ -10354,6 +10550,21 @@ move_deaths (x, from_cuid, to_insn, pnotes)
                               gen_rtx (REG, reg_raw_mode[i], i),
                               REG_NOTES (where_dead));
            }
+         /* If we didn't find any note, and we have a multi-reg hard
+            register, then to be safe we must check for REG_DEAD notes
+            for each register other than the first.  They could have
+            their own REG_DEAD notes lying around.  */
+         else if (note == 0 && regno < FIRST_PSEUDO_REGISTER
+                  && HARD_REGNO_NREGS (regno, GET_MODE (x)) > 1)
+           {
+             int ourend = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
+             int i;
+             rtx oldnotes = 0;
+
+             for (i = regno + 1; i < ourend; i++)
+               move_deaths (gen_rtx (REG, reg_raw_mode[i], i),
+                            from_cuid, to_insn, &oldnotes);
+           }
 
          if (note != 0 && GET_MODE (XEXP (note, 0)) == GET_MODE (x))
            {
@@ -10674,7 +10885,10 @@ distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1)
                         modified the register.  */
 
                      if (set != 0 && ! side_effects_p (SET_SRC (set))
-                         && rtx_equal_p (XEXP (note, 0), SET_DEST (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)))))
                        {
                          /* Move the notes and links of TEM elsewhere.
                             This might delete other dead insns recursively. 
@@ -10708,6 +10922,22 @@ distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1)
                             && find_reg_fusage (tem, USE, XEXP (note, 0))))
                  {
                    place = tem;
+
+                   /* If we are doing a 3->2 combination, and we have a
+                      register which formerly died in i3 and was not used
+                      by i2, which now no longer dies in i3 and is used in
+                      i2 but does not die in i2, and place is between i2
+                      and i3, then we may need to move a link from place to
+                      i2.  */
+                   if (i2 && INSN_UID (place) <= max_uid_cuid
+                       && INSN_CUID (place) > INSN_CUID (i2)
+                       && from_insn && INSN_CUID (from_insn) > INSN_CUID (i2)
+                       && reg_referenced_p (XEXP (note, 0), PATTERN (i2)))
+                     {
+                       rtx links = LOG_LINKS (place);
+                       LOG_LINKS (place) = 0;
+                       distribute_links (links);
+                     }
                    break;
                  }
                }
@@ -10966,6 +11196,22 @@ distribute_links (links)
     }
 }
 \f
+/* Compute INSN_CUID for INSN, which is an insn made by combine.  */
+
+static int
+insn_cuid (insn)
+     rtx insn;
+{
+  while (insn != 0 && INSN_UID (insn) > max_uid_cuid
+        && GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == USE)
+    insn = NEXT_INSN (insn);
+
+  if (INSN_UID (insn) > max_uid_cuid)
+    abort ();
+
+  return INSN_CUID (insn);
+}
+\f
 void
 dump_combine_stats (file)
      FILE *file;