OSDN Git Service

2000-07-21 Alexandre Petit-Bianco <apbianco@cygnus.com>
[pf3gnuchains/gcc-fork.git] / gcc / expmed.c
index 5fcfe64..778e990 100644 (file)
@@ -1,6 +1,7 @@
 /* Medium-level subroutines: convert bit-field store and extract
    and shifts, multiplies and divides to rtl instructions.
-   Copyright (C) 1987, 88, 89, 92, 93, 94, 1995 Free Software Foundation, Inc.
+   Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+   1999, 2000 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -16,12 +17,16 @@ 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.  */
 
 
 #include "config.h"
+#include "system.h"
+#include "toplev.h"
 #include "rtl.h"
 #include "tree.h"
+#include "tm_p.h"
 #include "flags.h"
 #include "insn-flags.h"
 #include "insn-codes.h"
@@ -30,17 +35,27 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "real.h"
 #include "recog.h"
 
-static void store_fixed_bit_field      PROTO((rtx, int, int, int, rtx, int));
-static void store_split_bit_field      PROTO((rtx, int, int, rtx, int));
-static rtx extract_fixed_bit_field     PROTO((enum machine_mode, rtx, int,
-                                              int, int, rtx, int, int));
-static rtx mask_rtx                    PROTO((enum machine_mode, int,
-                                              int, int));
-static rtx lshift_value                        PROTO((enum machine_mode, rtx,
-                                              int, int));
-static rtx extract_split_bit_field     PROTO((rtx, int, int, int, int));
-
-#define CEIL(x,y) (((x) + (y) - 1) / (y))
+static void store_fixed_bit_field      PARAMS ((rtx, unsigned HOST_WIDE_INT,
+                                                unsigned HOST_WIDE_INT,
+                                                unsigned HOST_WIDE_INT, rtx,
+                                                unsigned int));
+static void store_split_bit_field      PARAMS ((rtx, unsigned HOST_WIDE_INT,
+                                                unsigned HOST_WIDE_INT, rtx,
+                                                unsigned int));
+static rtx extract_fixed_bit_field     PARAMS ((enum machine_mode, rtx,
+                                                unsigned HOST_WIDE_INT,
+                                                unsigned HOST_WIDE_INT,
+                                                unsigned HOST_WIDE_INT,
+                                                rtx, int, unsigned int));
+static rtx mask_rtx                    PARAMS ((enum machine_mode, int,
+                                                int, int));
+static rtx lshift_value                        PARAMS ((enum machine_mode, rtx,
+                                                int, int));
+static rtx extract_split_bit_field     PARAMS ((rtx, unsigned HOST_WIDE_INT,
+                                                unsigned HOST_WIDE_INT, int,
+                                                unsigned int));
+static void do_cmp_and_jump            PARAMS ((rtx, rtx, enum rtx_code,
+                                                enum machine_mode, rtx));
 
 /* Non-zero means divides or modulus operations are relatively cheap for
    powers of two, so don't use branches; emit the operation instead. 
@@ -50,7 +65,7 @@ static rtx extract_split_bit_field    PROTO((rtx, int, int, int, int));
 static int sdiv_pow2_cheap, smod_pow2_cheap;
 
 #ifndef SLOW_UNALIGNED_ACCESS
-#define SLOW_UNALIGNED_ACCESS STRICT_ALIGNMENT
+#define SLOW_UNALIGNED_ACCESS(MODE, ALIGN) STRICT_ALIGNMENT
 #endif
 
 /* For compilers that support multiple targets with different word sizes,
@@ -61,8 +76,8 @@ static int sdiv_pow2_cheap, smod_pow2_cheap;
 #define MAX_BITS_PER_WORD BITS_PER_WORD
 #endif
 
-/* Cost of various pieces of RTL.  Note that some of these are indexed by shift count,
-   and some by mode.  */
+/* Cost of various pieces of RTL.  Note that some of these are indexed by
+   shift count and some by mode.  */
 static int add_cost, negate_cost, zero_cost;
 static int shift_cost[MAX_BITS_PER_WORD];
 static int shiftadd_cost[MAX_BITS_PER_WORD];
@@ -75,10 +90,9 @@ static int mul_highpart_cost[NUM_MACHINE_MODES];
 void
 init_expmed ()
 {
-  char *free_point;
   /* This is "some random pseudo register" for purposes of calling recog
      to see what insns exist.  */
-  rtx reg = gen_rtx (REG, word_mode, 10000);
+  rtx reg = gen_rtx_REG (word_mode, 10000);
   rtx shift_insn, shiftadd_insn, shiftsub_insn;
   int dummy;
   int m;
@@ -86,37 +100,35 @@ init_expmed ()
 
   start_sequence ();
 
-  /* Since we are on the permanent obstack, we must be sure we save this
-     spot AFTER we call start_sequence, since it will reuse the rtl it
-     makes.  */
-
-  free_point = (char *) oballoc (0);
+  reg = gen_rtx_REG (word_mode, 10000);
 
   zero_cost = rtx_cost (const0_rtx, 0);
-  add_cost = rtx_cost (gen_rtx (PLUS, word_mode, reg, reg), SET);
-
-  shift_insn = emit_insn (gen_rtx (SET, VOIDmode, reg,
-                                  gen_rtx (ASHIFT, word_mode, reg,
-                                           const0_rtx)));
-
-  shiftadd_insn = emit_insn (gen_rtx (SET, VOIDmode, reg,
-                                     gen_rtx (PLUS, word_mode,
-                                              gen_rtx (MULT, word_mode,
-                                                       reg, const0_rtx),
-                                              reg)));
-
-  shiftsub_insn = emit_insn (gen_rtx (SET, VOIDmode, reg,
-                                     gen_rtx (MINUS, word_mode,
-                                              gen_rtx (MULT, word_mode,
-                                                        reg, const0_rtx),
-                                               reg)));
+  add_cost = rtx_cost (gen_rtx_PLUS (word_mode, reg, reg), SET);
+
+  shift_insn = emit_insn (gen_rtx_SET (VOIDmode, reg,
+                                      gen_rtx_ASHIFT (word_mode, reg,
+                                                      const0_rtx)));
+
+  shiftadd_insn
+    = emit_insn (gen_rtx_SET (VOIDmode, reg,
+                             gen_rtx_PLUS (word_mode,
+                                           gen_rtx_MULT (word_mode,
+                                                         reg, const0_rtx),
+                                           reg)));
+
+  shiftsub_insn
+    = emit_insn (gen_rtx_SET (VOIDmode, reg,
+                             gen_rtx_MINUS (word_mode,
+                                            gen_rtx_MULT (word_mode,
+                                                          reg, const0_rtx),
+                                            reg)));
 
   init_recog ();
 
   shift_cost[0] = 0;
   shiftadd_cost[0] = shiftsub_cost[0] = add_cost;
 
-  for (m = 1; m < BITS_PER_WORD; m++)
+  for (m = 1; m < MAX_BITS_PER_WORD; m++)
     {
       shift_cost[m] = shiftadd_cost[m] = shiftsub_cost[m] = 32000;
 
@@ -135,44 +147,45 @@ init_expmed ()
        shiftsub_cost[m] = rtx_cost (SET_SRC (PATTERN (shiftsub_insn)), SET);
     }
 
-  negate_cost = rtx_cost (gen_rtx (NEG, word_mode, reg), SET);
+  negate_cost = rtx_cost (gen_rtx_NEG (word_mode, reg), SET);
 
   sdiv_pow2_cheap
-    = (rtx_cost (gen_rtx (DIV, word_mode, reg, GEN_INT (32)), SET)
+    = (rtx_cost (gen_rtx_DIV (word_mode, reg, GEN_INT (32)), SET)
        <= 2 * add_cost);
   smod_pow2_cheap
-    = (rtx_cost (gen_rtx (MOD, word_mode, reg, GEN_INT (32)), SET)
+    = (rtx_cost (gen_rtx_MOD (word_mode, reg, GEN_INT (32)), SET)
        <= 2 * add_cost);
 
   for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
        mode != VOIDmode;
        mode = GET_MODE_WIDER_MODE (mode))
     {
-      reg = gen_rtx (REG, mode, 10000);
-      div_cost[(int) mode] = rtx_cost (gen_rtx (UDIV, mode, reg, reg), SET);
-      mul_cost[(int) mode] = rtx_cost (gen_rtx (MULT, mode, reg, reg), SET);
+      reg = gen_rtx_REG (mode, 10000);
+      div_cost[(int) mode] = rtx_cost (gen_rtx_UDIV (mode, reg, reg), SET);
+      mul_cost[(int) mode] = rtx_cost (gen_rtx_MULT (mode, reg, reg), SET);
       wider_mode = GET_MODE_WIDER_MODE (mode);
       if (wider_mode != VOIDmode)
        {
          mul_widen_cost[(int) wider_mode]
-           = rtx_cost (gen_rtx (MULT, wider_mode,
-                                gen_rtx (ZERO_EXTEND, wider_mode, reg),
-                                gen_rtx (ZERO_EXTEND, wider_mode, reg)),
+           = rtx_cost (gen_rtx_MULT (wider_mode,
+                                     gen_rtx_ZERO_EXTEND (wider_mode, reg),
+                                     gen_rtx_ZERO_EXTEND (wider_mode, reg)),
                        SET);
          mul_highpart_cost[(int) mode]
-           = rtx_cost (gen_rtx (TRUNCATE, mode,
-                                gen_rtx (LSHIFTRT, wider_mode,
-                                         gen_rtx (MULT, wider_mode,
-                                                  gen_rtx (ZERO_EXTEND, wider_mode, reg),
-                                                  gen_rtx (ZERO_EXTEND, wider_mode, reg)),
-                                         GEN_INT (GET_MODE_BITSIZE (mode)))),
+           = rtx_cost (gen_rtx_TRUNCATE
+                       (mode,
+                        gen_rtx_LSHIFTRT (wider_mode,
+                                          gen_rtx_MULT (wider_mode,
+                                                        gen_rtx_ZERO_EXTEND
+                                                        (wider_mode, reg),
+                                                        gen_rtx_ZERO_EXTEND
+                                                        (wider_mode, reg)),
+                                          GEN_INT (GET_MODE_BITSIZE (mode)))),
                        SET);
        }
     }
 
-  /* Free the objects we just allocated.  */
   end_sequence ();
-  obfree (free_point);
 }
 
 /* Return an rtx representing minus the value of X.
@@ -184,52 +197,53 @@ negate_rtx (mode, x)
      enum machine_mode mode;
      rtx x;
 {
-  if (GET_CODE (x) == CONST_INT)
-    {
-      HOST_WIDE_INT val = - INTVAL (x);
-      if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT)
-       {
-         /* Sign extend the value from the bits that are significant.  */
-         if (val & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)))
-           val |= (HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (mode);
-         else
-           val &= ((HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (mode)) - 1;
-       }
-      return GEN_INT (val);
-    }
-  else
-    return expand_unop (GET_MODE (x), neg_optab, x, NULL_RTX, 0);
+  rtx result = simplify_unary_operation (NEG, mode, x, mode);
+
+  if (result == 0)
+    result = expand_unop (mode, neg_optab, x, NULL_RTX, 0);
+
+  return result;
 }
 \f
 /* Generate code to store value from rtx VALUE
    into a bit-field within structure STR_RTX
    containing BITSIZE bits starting at bit BITNUM.
    FIELDMODE is the machine-mode of the FIELD_DECL node for this field.
-   ALIGN is the alignment that STR_RTX is known to have, measured in bytes.
+   ALIGN is the alignment that STR_RTX is known to have.
    TOTAL_SIZE is the size of the structure in bytes, or -1 if varying.  */
 
 /* ??? Note that there are two different ideas here for how
    to determine the size to count bits within, for a register.
    One is BITS_PER_WORD, and the other is the size of operand 3
-   of the insv pattern.  (The latter assumes that an n-bit machine
-   will be able to insert bit fields up to n bits wide.)
-   It isn't certain that either of these is right.
-   extract_bit_field has the same quandary.  */
+   of the insv pattern.
+
+   If operand 3 of the insv pattern is VOIDmode, then we will use BITS_PER_WORD
+   else, we use the mode of operand 3.  */
 
 rtx
 store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
      rtx str_rtx;
-     register int bitsize;
-     int bitnum;
+     unsigned HOST_WIDE_INT bitsize;
+     unsigned HOST_WIDE_INT bitnum;
      enum machine_mode fieldmode;
      rtx value;
-     int align;
-     int total_size;
+     unsigned int align;
+     HOST_WIDE_INT total_size;
 {
-  int unit = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD;
-  register int offset = bitnum / unit;
-  register int bitpos = bitnum % unit;
+  unsigned int unit
+    = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD;
+  unsigned HOST_WIDE_INT offset = bitnum / unit;
+  unsigned HOST_WIDE_INT bitpos = bitnum % unit;
   register rtx op0 = str_rtx;
+#ifdef HAVE_insv
+  unsigned HOST_WIDE_INT insv_bitsize;
+  enum machine_mode op_mode;
+
+  op_mode = insn_data[(int) CODE_FOR_insv].operand[3].mode;
+  if (op_mode == VOIDmode)
+    op_mode = word_mode;
+  insv_bitsize = GET_MODE_BITSIZE (op_mode);
+#endif
 
   if (GET_CODE (str_rtx) == MEM && ! MEM_IN_STRUCT_P (str_rtx))
     abort ();
@@ -253,6 +267,21 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
       op0 = SUBREG_REG (op0);
     }
 
+  /* Make sure we are playing with integral modes.  Pun with subregs
+     if we aren't.  */
+  {
+    enum machine_mode imode = int_mode_for_mode (GET_MODE (op0));
+    if (imode != GET_MODE (op0))
+      {
+       if (GET_CODE (op0) == MEM)
+         op0 = change_address (op0, imode, NULL_RTX);
+       else if (imode != BLKmode)
+         op0 = gen_lowpart (imode, op0);
+       else
+         abort ();
+      }
+  }
+
   /* If OP0 is a register, BITPOS must count within a word.
      But as we have it, it counts within whatever size OP0 now has.
      On a bigendian machine, these are not the same, so convert.  */
@@ -266,21 +295,35 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
   if (flag_force_mem)
     value = force_not_mem (value);
 
-  /* Note that the adjustment of BITPOS above has no effect on whether
-     BITPOS is 0 in a REG bigger than a word.  */
-  if (GET_MODE_SIZE (fieldmode) >= UNITS_PER_WORD
+  if ((GET_MODE_SIZE (fieldmode) >= UNITS_PER_WORD
+       || (GET_MODE_SIZE (GET_MODE (op0)) == GET_MODE_SIZE (fieldmode)
+          && GET_MODE_SIZE (fieldmode) != 0))
       && (GET_CODE (op0) != MEM
-         || ! SLOW_UNALIGNED_ACCESS
+         || ! SLOW_UNALIGNED_ACCESS (fieldmode, align)
          || (offset * BITS_PER_UNIT % bitsize == 0
-             && align % GET_MODE_SIZE (fieldmode) == 0))
-      && bitpos == 0 && bitsize == GET_MODE_BITSIZE (fieldmode))
+             && align % GET_MODE_BITSIZE (fieldmode) == 0))
+      && (BYTES_BIG_ENDIAN ? bitpos + bitsize == unit : bitpos == 0)
+      && bitsize == GET_MODE_BITSIZE (fieldmode))
     {
       /* Storing in a full-word or multi-word field in a register
+        can be done with just SUBREG.  Also, storing in the entire object
         can be done with just SUBREG.  */
       if (GET_MODE (op0) != fieldmode)
        {
+         if (GET_CODE (op0) == SUBREG)
+           {
+             if (GET_MODE (SUBREG_REG (op0)) == fieldmode
+                 || GET_MODE_CLASS (fieldmode) == MODE_INT
+                 || GET_MODE_CLASS (fieldmode) == MODE_PARTIAL_INT)
+               op0 = SUBREG_REG (op0);
+             else
+               /* Else we've got some float mode source being extracted into
+                  a different float mode destination -- this combination of
+                  subregs results in Severe Tire Damage.  */
+               abort ();
+           }
          if (GET_CODE (op0) == REG)
-           op0 = gen_rtx (SUBREG, fieldmode, op0, offset);
+           op0 = gen_rtx_SUBREG (fieldmode, op0, offset);
          else
            op0 = change_address (op0, fieldmode,
                                  plus_constant (XEXP (op0, 0), offset));
@@ -295,10 +338,11 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
   if (GET_CODE (op0) != MEM
       && (BYTES_BIG_ENDIAN ? bitpos + bitsize == unit : bitpos == 0)
       && bitsize == GET_MODE_BITSIZE (fieldmode)
-      && (GET_MODE (op0) == fieldmode
-         || (movstrict_optab->handlers[(int) fieldmode].insn_code
-             != CODE_FOR_nothing)))
+      && (movstrict_optab->handlers[(int) fieldmode].insn_code
+         != CODE_FOR_nothing))
     {
+      int icode = movstrict_optab->handlers[(int) fieldmode].insn_code;
+
       /* Get appropriate low part of the value being stored.  */
       if (GET_CODE (value) == CONST_INT || GET_CODE (value) == REG)
        value = gen_lowpart (fieldmode, value);
@@ -307,16 +351,25 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
                 || GET_CODE (value) == CONST))
        value = convert_to_mode (fieldmode, value, 0);
 
-      if (GET_MODE (op0) == fieldmode)
-       emit_move_insn (op0, value);
-      else
+      if (! (*insn_data[icode].operand[1].predicate) (value, fieldmode))
+       value = copy_to_mode_reg (fieldmode, value);
+
+      if (GET_CODE (op0) == SUBREG)
        {
-         int icode = movstrict_optab->handlers[(int) fieldmode].insn_code;
-         if(! (*insn_operand_predicate[icode][1]) (value, fieldmode))
-           value = copy_to_mode_reg (fieldmode, value);
-         emit_insn (GEN_FCN (icode)
-                  (gen_rtx (SUBREG, fieldmode, op0, offset), value));
+         if (GET_MODE (SUBREG_REG (op0)) == fieldmode
+             || GET_MODE_CLASS (fieldmode) == MODE_INT
+             || GET_MODE_CLASS (fieldmode) == MODE_PARTIAL_INT)
+           op0 = SUBREG_REG (op0);
+         else
+           /* Else we've got some float mode source being extracted into
+              a different float mode destination -- this combination of
+              subregs results in Severe Tire Damage.  */
+           abort ();
        }
+
+      emit_insn (GEN_FCN (icode)
+                (gen_rtx_SUBREG (fieldmode, op0, offset), value));
+
       return value;
     }
 
@@ -330,10 +383,9 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
         be less than full.
         However, only do that if the value is not BLKmode.  */
 
-      int backwards = WORDS_BIG_ENDIAN && fieldmode != BLKmode;
-
-      int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;
-      int i;
+      unsigned int backwards = WORDS_BIG_ENDIAN && fieldmode != BLKmode;
+      unsigned int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;
+      unsigned int i;
 
       /* This is the mode we must force value to, so that there will be enough
         subwords to extract.  Note that fieldmode will often (always?) be
@@ -346,10 +398,13 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
        {
          /* If I is 0, use the low-order word in both field and target;
             if I is 1, use the next to lowest word; and so on.  */
-         int wordnum = (backwards ? nwords - i - 1 : i);
-         int bit_offset = (backwards
-                           ? MAX (bitsize - (i + 1) * BITS_PER_WORD, 0)
-                           : i * BITS_PER_WORD);
+         unsigned int wordnum = (backwards ? nwords - i - 1 : i);
+         unsigned int bit_offset = (backwards
+                           ? MAX ((int) bitsize - ((int) i + 1)
+                                  * BITS_PER_WORD,
+                                  0)
+                           : (int) i * BITS_PER_WORD);
+
          store_bit_field (op0, MIN (BITS_PER_WORD,
                                     bitsize - i * BITS_PER_WORD),
                           bitnum + bit_offset, word_mode,
@@ -368,12 +423,27 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
   /* OFFSET is the number of words or bytes (UNIT says which)
      from STR_RTX to the first word or byte containing part of the field.  */
 
-  if (GET_CODE (op0) == REG)
+  if (GET_CODE (op0) != MEM)
     {
       if (offset != 0
          || GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD)
-       op0 = gen_rtx (SUBREG, TYPE_MODE (type_for_size (BITS_PER_WORD, 0)),
-                      op0, offset);
+       {
+         if (GET_CODE (op0) != REG)
+           {
+             /* Since this is a destination (lvalue), we can't copy it to a
+                pseudo.  We can trivially remove a SUBREG that does not
+                change the size of the operand.  Such a SUBREG may have been
+                added above.  Otherwise, abort.  */
+             if (GET_CODE (op0) == SUBREG
+                 && (GET_MODE_SIZE (GET_MODE (op0))
+                     == GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))
+               op0 = SUBREG_REG (op0);
+             else
+               abort ();
+           }
+         op0 = gen_rtx_SUBREG (mode_for_size (BITS_PER_WORD, MODE_INT, 0),
+                               op0, offset);
+       }
       offset = 0;
     }
   else
@@ -389,7 +459,7 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
     {
       if (GET_CODE (value) != REG)
        value = copy_to_reg (value);
-      value = gen_rtx (SUBREG, word_mode, value, 0);
+      value = gen_rtx_SUBREG (word_mode, value, 0);
     }
 
   /* Now OFFSET is nonzero only if OP0 is memory
@@ -397,32 +467,34 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
 
 #ifdef HAVE_insv
   if (HAVE_insv
+      && GET_MODE (value) != BLKmode
       && !(bitsize == 1 && GET_CODE (value) == CONST_INT)
       /* Ensure insv's size is wide enough for this field.  */
-      && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_insv][3])
-         >= bitsize)
+      && (insv_bitsize >= bitsize)
       && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
-           && (bitsize + bitpos
-               > GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_insv][3]))))
+           && (bitsize + bitpos > insv_bitsize)))
     {
       int xbitpos = bitpos;
       rtx value1;
       rtx xop0 = op0;
       rtx last = get_last_insn ();
       rtx pat;
-      enum machine_mode maxmode
-       = insn_operand_mode[(int) CODE_FOR_insv][3];
-
+      enum machine_mode maxmode;
       int save_volatile_ok = volatile_ok;
+
+      maxmode = insn_data[(int) CODE_FOR_insv].operand[3].mode;
+      if (maxmode == VOIDmode)
+       maxmode = word_mode;
+
       volatile_ok = 1;
 
-      /* If this machine's insv can only insert into a register, or if we
-        are to force MEMs into a register, copy OP0 into a register and
-        save it back later.  */
+      /* If this machine's insv can only insert into a register, copy OP0
+        into a register and save it back later.  */
+      /* This used to check flag_force_mem, but that was a serious
+        de-optimization now that flag_force_mem is enabled by -O2.  */
       if (GET_CODE (op0) == MEM
-         && (flag_force_mem
-             || ! ((*insn_operand_predicate[(int) CODE_FOR_insv][0])
-                   (op0, VOIDmode))))
+         && ! ((*insn_data[(int) CODE_FOR_insv].operand[0].predicate)
+               (op0, VOIDmode)))
        {
          rtx tempreg;
          enum machine_mode bestmode;
@@ -435,13 +507,14 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
          if (GET_MODE (op0) == BLKmode
              || GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (maxmode))
            bestmode
-             = get_best_mode (bitsize, bitnum, align * BITS_PER_UNIT, maxmode,
+             = get_best_mode (bitsize, bitnum, align, maxmode,
                               MEM_VOLATILE_P (op0));
          else
            bestmode = GET_MODE (op0);
 
          if (bestmode == VOIDmode
-             || (SLOW_UNALIGNED_ACCESS && GET_MODE_SIZE (bestmode) > align))
+             || (SLOW_UNALIGNED_ACCESS (bestmode, align)
+                 && GET_MODE_BITSIZE (bestmode) > align))
            goto insv_loses;
 
          /* Adjust address to point to the containing unit of that mode.  */
@@ -452,7 +525,8 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
          op0 = change_address (op0, bestmode, 
                                plus_constant (XEXP (op0, 0), offset));
 
-         /* Fetch that unit, store the bitfield in it, then store the unit.  */
+         /* Fetch that unit, store the bitfield in it, then store
+            the unit.  */
          tempreg = copy_to_reg (op0);
          store_bit_field (tempreg, bitsize, bitpos, fieldmode, value,
                           align, total_size);
@@ -471,9 +545,9 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
       if (GET_CODE (xop0) == SUBREG)
        /* We can't just change the mode, because this might clobber op0,
           and we will need the original value of op0 if insv fails.  */
-       xop0 = gen_rtx (SUBREG, maxmode, SUBREG_REG (xop0), SUBREG_WORD (xop0));
+       xop0 = gen_rtx_SUBREG (maxmode, SUBREG_REG (xop0), SUBREG_WORD (xop0));
       if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode)
-       xop0 = gen_rtx (SUBREG, maxmode, xop0, 0);
+       xop0 = gen_rtx_SUBREG (maxmode, xop0, 0);
 
       /* On big-endian machines, we count bits from the most significant.
         If the bit field insn does not, we must invert.  */
@@ -502,8 +576,8 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
                {
                  /* Avoid making subreg of a subreg, or of a mem.  */
                  if (GET_CODE (value1) != REG)
-               value1 = copy_to_reg (value1);
-                 value1 = gen_rtx (SUBREG, maxmode, value1, 0);
+                   value1 = copy_to_reg (value1);
+                 value1 = gen_rtx_SUBREG (maxmode, value1, 0);
                }
              else
                value1 = gen_lowpart (maxmode, value1);
@@ -518,7 +592,7 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
 
       /* If this machine's insv insists on a register,
         get VALUE1 into a register.  */
-      if (! ((*insn_operand_predicate[(int) CODE_FOR_insv][3])
+      if (! ((*insn_data[(int) CODE_FOR_insv].operand[3].predicate)
             (value1, maxmode)))
        value1 = force_reg (maxmode, value1);
 
@@ -547,23 +621,26 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
     (If OP0 is a register, it may be a full word or a narrower mode,
      but BITPOS still counts within a full word,
      which is significant on bigendian machines.)
-   STRUCT_ALIGN is the alignment the structure is known to have (in bytes).
+   STRUCT_ALIGN is the alignment the structure is known to have.
 
    Note that protect_from_queue has already been done on OP0 and VALUE.  */
 
 static void
 store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)
      register rtx op0;
-     register int offset, bitsize, bitpos;
+     unsigned HOST_WIDE_INT offset, bitsize, bitpos;
      register rtx value;
-     int struct_align;
+     unsigned int struct_align;
 {
   register enum machine_mode mode;
-  int total_bits = BITS_PER_WORD;
+  unsigned int total_bits = BITS_PER_WORD;
   rtx subtarget, temp;
   int all_zero = 0;
   int all_one = 0;
 
+  if (! SLOW_UNALIGNED_ACCESS (word_mode, struct_align))
+    struct_align = BIGGEST_ALIGNMENT;
+    
   /* There is a case not handled here:
      a structure with a known alignment of just a halfword
      and a field split across two aligned halfwords within the structure.
@@ -590,7 +667,7 @@ store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)
         a word, we won't be doing the extraction the normal way.  */
 
       mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT,
-                           struct_align * BITS_PER_UNIT, word_mode,
+                           struct_align, word_mode,
                            GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0));
 
       if (mode == VOIDmode)
@@ -606,7 +683,7 @@ store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)
       total_bits = GET_MODE_BITSIZE (mode);
 
       /* Make sure bitpos is valid for the chosen mode.  Adjust BITPOS to
-        be be in the range 0 to total_bits-1, and put any excess bytes in
+        be in the range 0 to total_bits-1, and put any excess bytes in
         OFFSET.  */
       if (bitpos >= total_bits)
        {
@@ -714,7 +791,7 @@ store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)
    BITSIZE is the field width; BITPOS the position of its first bit
    (within the word).
    VALUE is the value to store.
-   ALIGN is the known alignment of OP0, measured in bytes.
+   ALIGN is the known alignment of OP0.
    This is also the size of the memory objects to be used.
 
    This does not yet handle fields wider than BITS_PER_WORD.  */
@@ -722,19 +799,19 @@ store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)
 static void
 store_split_bit_field (op0, bitsize, bitpos, value, align)
      rtx op0;
-     int bitsize, bitpos;
+     unsigned HOST_WIDE_INT bitsize, bitpos;
      rtx value;
-     int align;
+     unsigned int align;
 {
-  int unit;
-  int bitsdone = 0;
+  unsigned int unit;
+  unsigned int bitsdone = 0;
 
   /* Make sure UNIT isn't larger than BITS_PER_WORD, we can only handle that
      much at a time.  */
   if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
     unit = BITS_PER_WORD;
   else
-    unit = MIN (align * BITS_PER_UNIT, BITS_PER_WORD);
+    unit = MIN (align, BITS_PER_WORD);
 
   /* If VALUE is a constant other than a CONST_INT, get it into a register in
      WORD_MODE.  If we can do this using gen_lowpart_common, do so.  Note
@@ -751,13 +828,15 @@ store_split_bit_field (op0, bitsize, bitpos, value, align)
                                               ? GET_MODE (value)
                                               : word_mode, value));
     }
+  else if (GET_CODE (value) == ADDRESSOF)
+    value = copy_to_reg (value);
 
   while (bitsdone < bitsize)
     {
-      int thissize;
+      unsigned HOST_WIDE_INT thissize;
       rtx part, word;
-      int thispos;
-      int offset;
+      unsigned HOST_WIDE_INT thispos;
+      unsigned HOST_WIDE_INT offset;
 
       offset = (bitpos + bitsdone) / unit;
       thispos = (bitpos + bitsdone) % unit;
@@ -770,6 +849,16 @@ store_split_bit_field (op0, bitsize, bitpos, value, align)
 
       if (BYTES_BIG_ENDIAN)
        {
+         int total_bits;
+
+         /* We must do an endian conversion exactly the same way as it is
+            done in extract_bit_field, so that the two calls to
+            extract_fixed_bit_field will have comparable arguments.  */
+         if (GET_CODE (value) != MEM || GET_MODE (value) == BLKmode)
+           total_bits = BITS_PER_WORD;
+         else
+           total_bits = GET_MODE_BITSIZE (GET_MODE (value));
+
          /* Fetch successively less significant portions.  */
          if (GET_CODE (value) == CONST_INT)
            part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value))
@@ -778,11 +867,18 @@ store_split_bit_field (op0, bitsize, bitpos, value, align)
          else
            /* The args are chosen so that the last part includes the
               lsb.  Give extract_bit_field the value it needs (with
-              endianness compensation) to fetch the piece we want.  */
-           part = extract_fixed_bit_field (word_mode, value, 0, thissize,
-                                           GET_MODE_BITSIZE (GET_MODE (value))
-                                           - bitsize + bitsdone,
-                                           NULL_RTX, 1, align);
+              endianness compensation) to fetch the piece we want.
+
+              ??? We have no idea what the alignment of VALUE is, so
+              we have to use a guess.  */
+           part
+             = extract_fixed_bit_field
+               (word_mode, value, 0, thissize,
+                total_bits - bitsize + bitsdone, NULL_RTX, 1,
+                GET_MODE (value) == VOIDmode
+                ? UNITS_PER_WORD
+                : (GET_MODE (value) == BLKmode
+                   ? 1 : GET_MODE_ALIGNMENT (GET_MODE (value))));
        }
       else
        {
@@ -792,8 +888,13 @@ store_split_bit_field (op0, bitsize, bitpos, value, align)
                             >> bitsdone)
                            & (((HOST_WIDE_INT) 1 << thissize) - 1));
          else
-           part = extract_fixed_bit_field (word_mode, value, 0, thissize,
-                                           bitsdone, NULL_RTX, 1, align);
+           part
+             = extract_fixed_bit_field
+               (word_mode, value, 0, thissize, bitsdone, NULL_RTX, 1,
+                GET_MODE (value) == VOIDmode
+                ? UNITS_PER_WORD
+                : (GET_MODE (value) == BLKmode
+                   ? 1 : GET_MODE_ALIGNMENT (GET_MODE (value))));
        }
 
       /* If OP0 is a register, then handle OFFSET here.
@@ -837,7 +938,7 @@ store_split_bit_field (op0, bitsize, bitpos, value, align)
    TMODE is the mode the caller would like the value to have;
    but the value may be returned with type MODE instead.
 
-   ALIGN is the alignment that STR_RTX is known to have, measured in bytes.
+   ALIGN is the alignment that STR_RTX is known to have.
    TOTAL_SIZE is the size in bytes of the containing structure,
    or -1 if varying.
 
@@ -850,23 +951,44 @@ rtx
 extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
                   target, mode, tmode, align, total_size)
      rtx str_rtx;
-     register int bitsize;
-     int bitnum;
+     unsigned HOST_WIDE_INT bitsize;
+     unsigned HOST_WIDE_INT bitnum;
      int unsignedp;
      rtx target;
      enum machine_mode mode, tmode;
-     int align;
-     int total_size;
+     unsigned int align;
+     HOST_WIDE_INT total_size;
 {
-  int unit = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD;
-  register int offset = bitnum / unit;
-  register int bitpos = bitnum % unit;
+  unsigned int unit
+    = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD;
+  unsigned HOST_WIDE_INT offset = bitnum / unit;
+  unsigned HOST_WIDE_INT bitpos = bitnum % unit;
   register rtx op0 = str_rtx;
   rtx spec_target = target;
   rtx spec_target_subreg = 0;
+  enum machine_mode int_mode;
+#ifdef HAVE_extv
+  unsigned HOST_WIDE_INT extv_bitsize;
+  enum machine_mode extv_mode;
+#endif
+#ifdef HAVE_extzv
+  unsigned HOST_WIDE_INT extzv_bitsize;
+  enum machine_mode extzv_mode;
+#endif
 
-  if (GET_CODE (str_rtx) == MEM && ! MEM_IN_STRUCT_P (str_rtx))
-    abort ();
+#ifdef HAVE_extv
+  extv_mode = insn_data[(int) CODE_FOR_extv].operand[0].mode;
+  if (extv_mode == VOIDmode)
+    extv_mode = word_mode;
+  extv_bitsize = GET_MODE_BITSIZE (extv_mode);
+#endif
+
+#ifdef HAVE_extzv
+  extzv_mode = insn_data[(int) CODE_FOR_extzv].operand[0].mode;
+  if (extzv_mode == VOIDmode)
+    extzv_mode = word_mode;
+  extzv_bitsize = GET_MODE_BITSIZE (extzv_mode);
+#endif
 
   /* Discount the part of the structure before the desired byte.
      We need to know how many bytes are safe to reference after it.  */
@@ -883,6 +1005,8 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
 
       offset += SUBREG_WORD (op0);
 
+      inner_size = MIN (inner_size, BITS_PER_WORD);
+
       if (BYTES_BIG_ENDIAN && (outer_size < inner_size))
        {
          bitpos += inner_size - outer_size;
@@ -896,6 +1020,21 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
       op0 = SUBREG_REG (op0);
     }
 
+  /* Make sure we are playing with integral modes.  Pun with subregs
+     if we aren't.  */
+  {
+    enum machine_mode imode = int_mode_for_mode (GET_MODE (op0));
+    if (imode != GET_MODE (op0))
+      {
+       if (GET_CODE (op0) == MEM)
+         op0 = change_address (op0, imode, NULL_RTX);
+       else if (imode != BLKmode)
+         op0 = gen_lowpart (imode, op0);
+       else
+         abort ();
+      }
+  }
+
   /* ??? We currently assume TARGET is at least as big as BITSIZE.
      If that's wrong, the solution is to test for it and set TARGET to 0
      if needed.  */
@@ -903,8 +1042,8 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
   /* If OP0 is a register, BITPOS must count within a word.
      But as we have it, it counts within whatever size OP0 now has.
      On a bigendian machine, these are not the same, so convert.  */
-  if (BYTES_BIG_ENDIAN &&
-      GET_CODE (op0) != MEM
+  if (BYTES_BIG_ENDIAN
+      && GET_CODE (op0) != MEM
       && unit > GET_MODE_BITSIZE (GET_MODE (op0)))
     bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
 
@@ -914,25 +1053,44 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
      So too extracting a subword value in
      the least significant part of the register.  */
 
-  if ((GET_CODE (op0) == REG
+  if (((GET_CODE (op0) != MEM
+       && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
+                                 GET_MODE_BITSIZE (GET_MODE (op0))))
        || (GET_CODE (op0) == MEM
-          && (! SLOW_UNALIGNED_ACCESS
+          && (! SLOW_UNALIGNED_ACCESS (mode, align)
               || (offset * BITS_PER_UNIT % bitsize == 0
-                  && align * BITS_PER_UNIT % bitsize == 0))))
+                  && align % bitsize == 0))))
       && ((bitsize >= BITS_PER_WORD && bitsize == GET_MODE_BITSIZE (mode)
           && bitpos % BITS_PER_WORD == 0)
          || (mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0) != BLKmode
+             /* ??? The big endian test here is wrong.  This is correct
+                if the value is in a register, and if mode_for_size is not
+                the same mode as op0.  This causes us to get unnecessarily
+                inefficient code from the Thumb port when -mbig-endian.  */
              && (BYTES_BIG_ENDIAN
                  ? bitpos + bitsize == BITS_PER_WORD
                  : bitpos == 0))))
     {
       enum machine_mode mode1
-       = mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0);
+       = (VECTOR_MODE_P (tmode) ? mode
+          : mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0));
 
       if (mode1 != GET_MODE (op0))
        {
+         if (GET_CODE (op0) == SUBREG)
+           {
+             if (GET_MODE (SUBREG_REG (op0)) == mode1
+                 || GET_MODE_CLASS (mode1) == MODE_INT
+                 || GET_MODE_CLASS (mode1) == MODE_PARTIAL_INT)
+               op0 = SUBREG_REG (op0);
+             else
+               /* Else we've got some float mode source being extracted into
+                  a different float mode destination -- this combination of
+                  subregs results in Severe Tire Damage.  */
+               abort ();
+           }
          if (GET_CODE (op0) == REG)
-           op0 = gen_rtx (SUBREG, mode1, op0, offset);
+           op0 = gen_rtx_SUBREG (mode1, op0, offset);
          else
            op0 = change_address (op0, mode1,
                                  plus_constant (XEXP (op0, 0), offset));
@@ -951,31 +1109,35 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
         This is because the most significant word is the one which may
         be less than full.  */
 
-      int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;
-      int i;
+      unsigned int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;
+      unsigned int i;
 
       if (target == 0 || GET_CODE (target) != REG)
        target = gen_reg_rtx (mode);
 
+      /* Indicate for flow that the entire target reg is being set.  */
+      emit_insn (gen_rtx_CLOBBER (VOIDmode, target));
+
       for (i = 0; i < nwords; i++)
        {
          /* If I is 0, use the low-order word in both field and target;
             if I is 1, use the next to lowest word; and so on.  */
          /* Word number in TARGET to use.  */
-         int wordnum = (WORDS_BIG_ENDIAN
-                        ? GET_MODE_SIZE (GET_MODE (target)) / UNITS_PER_WORD - i - 1
-                        : i);
+         unsigned int wordnum
+           = (WORDS_BIG_ENDIAN
+              ? GET_MODE_SIZE (GET_MODE (target)) / UNITS_PER_WORD - i - 1
+              : i);
          /* Offset from start of field in OP0.  */
-         int bit_offset = (WORDS_BIG_ENDIAN
-                           ? MAX (0, bitsize - (i + 1) * BITS_PER_WORD)
-                           : i * BITS_PER_WORD);
+         unsigned int bit_offset = (WORDS_BIG_ENDIAN
+                                    ? MAX (0, ((int) bitsize - ((int) i + 1)
+                                               * (int) BITS_PER_WORD))
+                                    : (int) i * BITS_PER_WORD);
          rtx target_part = operand_subword (target, wordnum, 1, VOIDmode);
          rtx result_part
            = extract_bit_field (op0, MIN (BITS_PER_WORD,
                                           bitsize - i * BITS_PER_WORD),
-                                bitnum + bit_offset,
-                                1, target_part, mode, word_mode,
-                                align, total_size);
+                                bitnum + bit_offset, 1, target_part, mode,
+                                word_mode, align, total_size);
 
          if (target_part == 0)
            abort ();
@@ -990,7 +1152,7 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
             need to be zero'd out.  */
          if (GET_MODE_SIZE (GET_MODE (target)) > nwords * UNITS_PER_WORD)
            {
-             int i,total_words;
+             unsigned int i, total_words;
 
              total_words = GET_MODE_SIZE (GET_MODE (target)) / UNITS_PER_WORD;
              for (i = nwords; i < total_words; i++)
@@ -1012,20 +1174,33 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
                           NULL_RTX, 0);
     }
   
-  /* From here on we know the desired field is smaller than a word
-     so we can assume it is an integer.  So we can safely extract it as one
-     size of integer, if necessary, and then truncate or extend
-     to the size that is wanted.  */
+  /* From here on we know the desired field is smaller than a word.  */
+
+  /* Check if there is a correspondingly-sized integer field, so we can
+     safely extract it as one size of integer, if necessary; then
+     truncate or extend to the size that is wanted; then use SUBREGs or
+     convert_to_mode to get one of the modes we really wanted.  */
+  
+  int_mode = int_mode_for_mode (tmode);
+  if (int_mode == BLKmode)
+    int_mode = int_mode_for_mode (mode);
+  if (int_mode == BLKmode)
+    abort();    /* Should probably push op0 out to memory and then
+                  do a load.  */
 
   /* OFFSET is the number of words or bytes (UNIT says which)
      from STR_RTX to the first word or byte containing part of the field.  */
 
-  if (GET_CODE (op0) == REG)
+  if (GET_CODE (op0) != MEM)
     {
       if (offset != 0
          || GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD)
-       op0 = gen_rtx (SUBREG, TYPE_MODE (type_for_size (BITS_PER_WORD, 0)),
-                      op0, offset);
+       {
+         if (GET_CODE (op0) != REG)
+           op0 = copy_to_reg (op0);
+         op0 = gen_rtx_SUBREG (mode_for_size (BITS_PER_WORD, MODE_INT, 0),
+                               op0, offset);
+       }
       offset = 0;
     }
   else
@@ -1039,22 +1214,23 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
     {
 #ifdef HAVE_extzv
       if (HAVE_extzv
-         && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extzv][0])
-             >= bitsize)
+         && (extzv_bitsize >= bitsize)
          && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
-               && (bitsize + bitpos
-                   > GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extzv][0]))))
+               && (bitsize + bitpos > extzv_bitsize)))
        {
-         int xbitpos = bitpos, xoffset = offset;
+         unsigned HOST_WIDE_INT xbitpos = bitpos, xoffset = offset;
          rtx bitsize_rtx, bitpos_rtx;
-         rtx last = get_last_insn();
+         rtx last = get_last_insn ();
          rtx xop0 = op0;
          rtx xtarget = target;
          rtx xspec_target = spec_target;
          rtx xspec_target_subreg = spec_target_subreg;
          rtx pat;
-         enum machine_mode maxmode
-           = insn_operand_mode[(int) CODE_FOR_extzv][0];
+         enum machine_mode maxmode;
+
+         maxmode = insn_data[(int) CODE_FOR_extzv].operand[0].mode;
+         if (maxmode == VOIDmode)
+           maxmode = word_mode;
 
          if (GET_CODE (xop0) == MEM)
            {
@@ -1062,9 +1238,8 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
              volatile_ok = 1;
 
              /* Is the memory operand acceptable?  */
-             if (flag_force_mem
-                 || ! ((*insn_operand_predicate[(int) CODE_FOR_extzv][1])
-                       (xop0, GET_MODE (xop0))))
+             if (! ((*insn_data[(int) CODE_FOR_extzv].operand[1].predicate)
+                    (xop0, GET_MODE (xop0))))
                {
                  /* No, load into a reg and extract from there.  */
                  enum machine_mode bestmode;
@@ -1078,14 +1253,14 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
                  if (GET_MODE (xop0) == BLKmode
                      || (GET_MODE_SIZE (GET_MODE (op0))
                          > GET_MODE_SIZE (maxmode)))
-                   bestmode = get_best_mode (bitsize, bitnum,
-                                             align * BITS_PER_UNIT, maxmode,
+                   bestmode = get_best_mode (bitsize, bitnum, align, maxmode,
                                              MEM_VOLATILE_P (xop0));
                  else
                    bestmode = GET_MODE (xop0);
 
                  if (bestmode == VOIDmode
-                     || (SLOW_UNALIGNED_ACCESS && GET_MODE_SIZE (bestmode) > align))
+                     || (SLOW_UNALIGNED_ACCESS (bestmode, align)
+                         && GET_MODE_BITSIZE (bestmode) > align))
                    goto extzv_loses;
 
                  /* Compute offset as multiple of this unit,
@@ -1112,9 +1287,9 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
          /* If op0 is a register, we need it in MAXMODE (which is usually
             SImode). to make it acceptable to the format of extzv.  */
          if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != maxmode)
-           abort ();
+           goto extzv_loses;
          if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode)
-           xop0 = gen_rtx (SUBREG, maxmode, xop0, 0);
+           xop0 = gen_rtx_SUBREG (maxmode, xop0, 0);
 
          /* On big-endian machines, we count bits from the most significant.
             If the bit field insn does not, we must invert.  */
@@ -1147,7 +1322,7 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
 
          /* If this machine's extzv insists on a register target,
             make sure we have one.  */
-         if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][0])
+         if (! ((*insn_data[(int) CODE_FOR_extzv].operand[0].predicate)
                 (xtarget, maxmode)))
            xtarget = gen_reg_rtx (maxmode);
 
@@ -1166,40 +1341,41 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
          else
            {
              delete_insns_since (last);
-             target = extract_fixed_bit_field (tmode, op0, offset, bitsize,
+             target = extract_fixed_bit_field (int_mode, op0, offset, bitsize,
                                                bitpos, target, 1, align);
            }
        }
       else
         extzv_loses:
 #endif
-       target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
-                                         target, 1, align);
+      target = extract_fixed_bit_field (int_mode, op0, offset, bitsize, 
+                                       bitpos, target, 1, align);
     }
   else
     {
 #ifdef HAVE_extv
       if (HAVE_extv
-         && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extv][0])
-             >= bitsize)
+         && (extv_bitsize >= bitsize)
          && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
-               && (bitsize + bitpos
-                   > GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extv][0]))))
+               && (bitsize + bitpos > extv_bitsize)))
        {
          int xbitpos = bitpos, xoffset = offset;
          rtx bitsize_rtx, bitpos_rtx;
-         rtx last = get_last_insn();
+         rtx last = get_last_insn ();
          rtx xop0 = op0, xtarget = target;
          rtx xspec_target = spec_target;
          rtx xspec_target_subreg = spec_target_subreg;
          rtx pat;
-         enum machine_mode maxmode
-           = insn_operand_mode[(int) CODE_FOR_extv][0];
+         enum machine_mode maxmode;
+
+         maxmode = insn_data[(int) CODE_FOR_extv].operand[0].mode;
+         if (maxmode == VOIDmode)
+           maxmode = word_mode;
 
          if (GET_CODE (xop0) == MEM)
            {
              /* Is the memory operand acceptable?  */
-             if (! ((*insn_operand_predicate[(int) CODE_FOR_extv][1])
+             if (! ((*insn_data[(int) CODE_FOR_extv].operand[1].predicate)
                     (xop0, GET_MODE (xop0))))
                {
                  /* No, load into a reg and extract from there.  */
@@ -1214,14 +1390,14 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
                  if (GET_MODE (xop0) == BLKmode
                      || (GET_MODE_SIZE (GET_MODE (op0))
                          > GET_MODE_SIZE (maxmode)))
-                   bestmode = get_best_mode (bitsize, bitnum,
-                                             align * BITS_PER_UNIT, maxmode,
+                   bestmode = get_best_mode (bitsize, bitnum, align, maxmode,
                                              MEM_VOLATILE_P (xop0));
                  else
                    bestmode = GET_MODE (xop0);
 
                  if (bestmode == VOIDmode
-                     || (SLOW_UNALIGNED_ACCESS && GET_MODE_SIZE (bestmode) > align))
+                     || (SLOW_UNALIGNED_ACCESS (bestmode, align)
+                         && GET_MODE_BITSIZE (bestmode) > align))
                    goto extv_loses;
 
                  /* Compute offset as multiple of this unit,
@@ -1246,9 +1422,9 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
          /* If op0 is a register, we need it in MAXMODE (which is usually
             SImode) to make it acceptable to the format of extv.  */
          if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != maxmode)
-           abort ();
+           goto extv_loses;
          if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode)
-           xop0 = gen_rtx (SUBREG, maxmode, xop0, 0);
+           xop0 = gen_rtx_SUBREG (maxmode, xop0, 0);
 
          /* On big-endian machines, we count bits from the most significant.
             If the bit field insn does not, we must invert.  */
@@ -1282,7 +1458,7 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
 
          /* If this machine's extv insists on a register target,
             make sure we have one.  */
-         if (! ((*insn_operand_predicate[(int) CODE_FOR_extv][0])
+         if (! ((*insn_data[(int) CODE_FOR_extv].operand[0].predicate)
                 (xtarget, maxmode)))
            xtarget = gen_reg_rtx (maxmode);
 
@@ -1301,15 +1477,15 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
          else
            {
              delete_insns_since (last);
-             target = extract_fixed_bit_field (tmode, op0, offset, bitsize,
+             target = extract_fixed_bit_field (int_mode, op0, offset, bitsize,
                                                bitpos, target, 0, align);
            }
        } 
       else
        extv_loses:
 #endif
-       target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
-                                         target, 0, align);
+      target = extract_fixed_bit_field (int_mode, op0, offset, bitsize, 
+                                       bitpos, target, 0, align);
     }
   if (target == spec_target)
     return target;
@@ -1327,7 +1503,7 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
                                    target, unsignedp);
          if (GET_CODE (target) != REG)
            target = copy_to_reg (target);
-         return gen_rtx (SUBREG, tmode, target, 0);
+         return gen_rtx_SUBREG (tmode, target, 0);
        }
       else
        return convert_to_mode (tmode, target, unsignedp);
@@ -1351,18 +1527,18 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
    and return TARGET, but this is not guaranteed.
    If TARGET is not used, create a pseudo-reg of mode TMODE for the value.
 
-   ALIGN is the alignment that STR_RTX is known to have, measured in bytes.  */
+   ALIGN is the alignment that STR_RTX is known to have.  */
 
 static rtx
 extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
                         target, unsignedp, align)
      enum machine_mode tmode;
      register rtx op0, target;
-     register int offset, bitsize, bitpos;
+     unsigned HOST_WIDE_INT offset, bitsize, bitpos;
      int unsignedp;
-     int align;
+     unsigned int align;
 {
-  int total_bits = BITS_PER_WORD;
+  unsigned int total_bits = BITS_PER_WORD;
   enum machine_mode mode;
 
   if (GET_CODE (op0) == SUBREG || GET_CODE (op0) == REG)
@@ -1378,8 +1554,8 @@ extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
         includes the entire field.  If such a mode would be larger than
         a word, we won't be doing the extraction the normal way.  */
 
-      mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT,
-                           align * BITS_PER_UNIT, word_mode,
+      mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT, align,
+                           word_mode,
                            GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0));
 
       if (mode == VOIDmode)
@@ -1392,7 +1568,7 @@ extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
       total_bits = GET_MODE_BITSIZE (mode);
 
       /* Make sure bitpos is valid for the chosen mode.  Adjust BITPOS to
-        be be in the range 0 to total_bits-1, and put any excess bytes in
+        be in the range 0 to total_bits-1, and put any excess bytes in
         OFFSET.  */
       if (bitpos >= total_bits)
        {
@@ -1451,7 +1627,7 @@ extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
 #ifdef SLOW_ZERO_EXTEND
          /* Always generate an `and' if
             we just zero-extended op0 and SLOW_ZERO_EXTEND, since it
-            will combine fruitfully with the zero-extend. */
+            will combine fruitfully with the zero-extend.  */
          || tmode != mode
 #endif
 #endif
@@ -1572,17 +1748,19 @@ lshift_value (mode, value, bitpos, bitsize)
    BITSIZE is the field width; BITPOS, position of its first bit, in the word.
    UNSIGNEDP is 1 if should zero-extend the contents; else sign-extend.
 
-   ALIGN is the known alignment of OP0, measured in bytes.
-   This is also the size of the memory objects to be used.  */
+   ALIGN is the known alignment of OP0.  This is also the size of the
+   memory objects to be used.  */
 
 static rtx
 extract_split_bit_field (op0, bitsize, bitpos, unsignedp, align)
      rtx op0;
-     int bitsize, bitpos, unsignedp, align;
+     unsigned HOST_WIDE_INT bitsize, bitpos;
+     int unsignedp;
+     unsigned int align;
 {
-  int unit;
-  int bitsdone = 0;
-  rtx result;
+  unsigned int unit;
+  unsigned int bitsdone = 0;
+  rtx result = NULL_RTX;
   int first = 1;
 
   /* Make sure UNIT isn't larger than BITS_PER_WORD, we can only handle that
@@ -1590,14 +1768,14 @@ extract_split_bit_field (op0, bitsize, bitpos, unsignedp, align)
   if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
     unit = BITS_PER_WORD;
   else
-    unit = MIN (align * BITS_PER_UNIT, BITS_PER_WORD);
+    unit = MIN (align, BITS_PER_WORD);
 
   while (bitsdone < bitsize)
     {
-      int thissize;
+      unsigned HOST_WIDE_INT thissize;
       rtx part, word;
-      int thispos;
-      int offset;
+      unsigned HOST_WIDE_INT thispos;
+      unsigned HOST_WIDE_INT offset;
 
       offset = (bitpos + bitsdone) / unit;
       thispos = (bitpos + bitsdone) % unit;
@@ -1727,12 +1905,18 @@ expand_shift (code, mode, shifted, amount, target, unsignedp)
 
   op1 = expand_expr (amount, NULL_RTX, VOIDmode, 0);
 
-#if SHIFT_COUNT_TRUNCATED
-  if (SHIFT_COUNT_TRUNCATED
-      && GET_CODE (op1) == CONST_INT
-      && (unsigned HOST_WIDE_INT) INTVAL (op1) >= GET_MODE_BITSIZE (mode))
-    op1 = GEN_INT ((unsigned HOST_WIDE_INT) INTVAL (op1)
-                  % GET_MODE_BITSIZE (mode));
+#ifdef SHIFT_COUNT_TRUNCATED
+  if (SHIFT_COUNT_TRUNCATED)
+    {
+      if (GET_CODE (op1) == CONST_INT
+          && ((unsigned HOST_WIDE_INT) INTVAL (op1) >=
+             (unsigned HOST_WIDE_INT) GET_MODE_BITSIZE (mode)))
+        op1 = GEN_INT ((unsigned HOST_WIDE_INT) INTVAL (op1)
+                      % GET_MODE_BITSIZE (mode));
+      else if (GET_CODE (op1) == SUBREG
+              && SUBREG_WORD (op1) == 0)
+       op1 = SUBREG_REG (op1);
+    }
 #endif
 
   if (op1 == const0_rtx)
@@ -1879,6 +2063,15 @@ struct algorithm
   char log[MAX_BITS_PER_WORD];
 };
 
+static void synth_mult                 PARAMS ((struct algorithm *,
+                                                unsigned HOST_WIDE_INT,
+                                                int));
+static unsigned HOST_WIDE_INT choose_multiplier PARAMS ((unsigned HOST_WIDE_INT,
+                                                        int, int,
+                                                        unsigned HOST_WIDE_INT *,
+                                                        int *, int *));
+static unsigned HOST_WIDE_INT invert_mod2n     PARAMS ((unsigned HOST_WIDE_INT,
+                                                        int));
 /* Compute and return the best algorithm for multiplying by T.
    The algorithm must cost less than cost_limit
    If retval.cost >= COST_LIMIT, no algorithm was found and all
@@ -1892,7 +2085,7 @@ synth_mult (alg_out, t, cost_limit)
 {
   int m;
   struct algorithm *alg_in, *best_alg;
-  unsigned int cost;
+  int cost;
   unsigned HOST_WIDE_INT q;
 
   /* Indicate that no algorithm is yet found.  If no algorithm
@@ -1959,10 +2152,16 @@ synth_mult (alg_out, t, cost_limit)
 
       for (w = 1; (w & t) != 0; w <<= 1)
        ;
-      if (w > 2
-         /* Reject the case where t is 3.
-            Thus we prefer addition in that case.  */
-         && t != 3)
+      /* If T was -1, then W will be zero after the loop.  This is another
+        case where T ends with ...111.  Handling this with (T + 1) and 
+        subtract 1 produces slightly better code and results in algorithm
+        selection much faster than treating it like the ...0111 case
+        below.  */
+      if (w == 0
+         || (w > 2
+             /* Reject the case where t is 3.
+                Thus we prefer addition in that case.  */
+             && t != 3))
        {
          /* T ends with ...111.  Multiply by (T + 1) and subtract 1.  */
 
@@ -2168,7 +2367,7 @@ expand_mult (mode, op0, op1, target, unsignedp)
         and then negate, do the multiplication directly, or do multiplication
         by OP1 - 1.  */
 
-      mult_cost = rtx_cost (gen_rtx (MULT, mode, op0, op1), SET);
+      mult_cost = rtx_cost (gen_rtx_MULT (mode, op0, op1), SET);
       mult_cost = MIN (12 * add_cost, mult_cost);
 
       synth_mult (&alg, val, mult_cost);
@@ -2225,7 +2424,8 @@ expand_mult (mode, op0, op1, target, unsignedp)
              rtx shift_subtarget = preserve ? 0 : accum;
              rtx add_target
                = (opno == alg.ops - 1 && target != 0 && variant != add_variant
-                 ? target : 0);
+                  && ! preserve)
+                 ? target : 0;
              rtx accum_target = preserve ? 0 : accum;
              
              switch (alg.op[opno])
@@ -2239,16 +2439,18 @@ expand_mult (mode, op0, op1, target, unsignedp)
                case alg_add_t_m2:
                  tem = expand_shift (LSHIFT_EXPR, mode, op0,
                                      build_int_2 (log, 0), NULL_RTX, 0);
-                 accum = force_operand (gen_rtx (PLUS, mode, accum, tem),
-                                        add_target ? add_target : accum_target);
+                 accum = force_operand (gen_rtx_PLUS (mode, accum, tem),
+                                        add_target
+                                        ? add_target : accum_target);
                  val_so_far += (HOST_WIDE_INT) 1 << log;
                  break;
 
                case alg_sub_t_m2:
                  tem = expand_shift (LSHIFT_EXPR, mode, op0,
                                      build_int_2 (log, 0), NULL_RTX, 0);
-                 accum = force_operand (gen_rtx (MINUS, mode, accum, tem),
-                                        add_target ? add_target : accum_target);
+                 accum = force_operand (gen_rtx_MINUS (mode, accum, tem),
+                                        add_target
+                                        ? add_target : accum_target);
                  val_so_far -= (HOST_WIDE_INT) 1 << log;
                  break;
 
@@ -2256,8 +2458,9 @@ expand_mult (mode, op0, op1, target, unsignedp)
                  accum = expand_shift (LSHIFT_EXPR, mode, accum,
                                        build_int_2 (log, 0), shift_subtarget,
                                        0);
-                 accum = force_operand (gen_rtx (PLUS, mode, accum, op0),
-                                        add_target ? add_target : accum_target);
+                 accum = force_operand (gen_rtx_PLUS (mode, accum, op0),
+                                        add_target
+                                        ? add_target : accum_target);
                  val_so_far = (val_so_far << log) + 1;
                  break;
 
@@ -2265,40 +2468,42 @@ expand_mult (mode, op0, op1, target, unsignedp)
                  accum = expand_shift (LSHIFT_EXPR, mode, accum,
                                        build_int_2 (log, 0), shift_subtarget,
                                        0);
-                 accum = force_operand (gen_rtx (MINUS, mode, accum, op0),
-                                        add_target ? add_target : accum_target);
+                 accum = force_operand (gen_rtx_MINUS (mode, accum, op0),
+                                        add_target
+                                        ? add_target : accum_target);
                  val_so_far = (val_so_far << log) - 1;
                  break;
 
                case alg_add_factor:
                  tem = expand_shift (LSHIFT_EXPR, mode, accum,
                                      build_int_2 (log, 0), NULL_RTX, 0);
-                 accum = force_operand (gen_rtx (PLUS, mode, accum, tem),
-                                        add_target ? add_target : accum_target);
+                 accum = force_operand (gen_rtx_PLUS (mode, accum, tem),
+                                        add_target
+                                        ? add_target : accum_target);
                  val_so_far += val_so_far << log;
                  break;
 
                case alg_sub_factor:
                  tem = expand_shift (LSHIFT_EXPR, mode, accum,
                                      build_int_2 (log, 0), NULL_RTX, 0);
-                 accum = force_operand (gen_rtx (MINUS, mode, tem, accum),
+                 accum = force_operand (gen_rtx_MINUS (mode, tem, accum),
                                         (add_target ? add_target
                                          : preserve ? 0 : tem));
                  val_so_far = (val_so_far << log) - val_so_far;
                  break;
 
                default:
-                 abort ();;
+                 abort ();
                }
 
              /* Write a REG_EQUAL note on the last insn so that we can cse
                 multiplication sequences.  */
 
              insn = get_last_insn ();
-             REG_NOTES (insn)
-               = gen_rtx (EXPR_LIST, REG_EQUAL,
-                          gen_rtx (MULT, mode, op0, GEN_INT (val_so_far)),
-                          REG_NOTES (insn));
+             set_unique_reg_note (insn, 
+                                  REG_EQUAL,
+                                  gen_rtx_MULT (mode, op0, 
+                                                GEN_INT (val_so_far)));
            }
 
          if (variant == negate_variant)
@@ -2309,7 +2514,7 @@ expand_mult (mode, op0, op1, target, unsignedp)
          else if (variant == add_variant)
            {
              val_so_far = val_so_far + 1;
-             accum = force_operand (gen_rtx (PLUS, mode, accum, op0), target);
+             accum = force_operand (gen_rtx_PLUS (mode, accum, op0), target);
            }
 
          if (val != val_so_far)
@@ -2363,11 +2568,12 @@ choose_multiplier (d, n, precision, multiplier_ptr, post_shift_ptr, lgup_ptr)
      int *post_shift_ptr;
      int *lgup_ptr;
 {
-  unsigned HOST_WIDE_INT mhigh_hi, mhigh_lo;
-  unsigned HOST_WIDE_INT mlow_hi, mlow_lo;
+  HOST_WIDE_INT mhigh_hi, mlow_hi;
+  unsigned HOST_WIDE_INT mhigh_lo, mlow_lo;
   int lgup, post_shift;
   int pow, pow2;
-  unsigned HOST_WIDE_INT nh, nl, dummy1, dummy2;
+  unsigned HOST_WIDE_INT nl, dummy1;
+  HOST_WIDE_INT nh, dummy2;
 
   /* lgup = ceil(log2(divisor)); */
   lgup = ceil_log2 (d);
@@ -2388,7 +2594,7 @@ choose_multiplier (d, n, precision, multiplier_ptr, post_shift_ptr, lgup_ptr)
   /* mlow = 2^(N + lgup)/d */
  if (pow >= HOST_BITS_PER_WIDE_INT)
     {
-      nh = (unsigned HOST_WIDE_INT) 1 << (pow - HOST_BITS_PER_WIDE_INT);
+      nh = (HOST_WIDE_INT) 1 << (pow - HOST_BITS_PER_WIDE_INT);
       nl = 0;
     }
   else
@@ -2401,7 +2607,7 @@ choose_multiplier (d, n, precision, multiplier_ptr, post_shift_ptr, lgup_ptr)
 
   /* mhigh = (2^(N + lgup) + 2^N + lgup - precision)/d */
   if (pow2 >= HOST_BITS_PER_WIDE_INT)
-    nh |= (unsigned HOST_WIDE_INT) 1 << (pow2 - HOST_BITS_PER_WIDE_INT);
+    nh |= (HOST_WIDE_INT) 1 << (pow2 - HOST_BITS_PER_WIDE_INT);
   else
     nl |= (unsigned HOST_WIDE_INT) 1 << pow2;
   div_and_round_double (TRUNC_DIV_EXPR, 1, nl, nh, d, (HOST_WIDE_INT) 0,
@@ -2455,7 +2661,7 @@ invert_mod2n (x, n)
      unsigned HOST_WIDE_INT x;
      int n;
 {
-  /* Solve x*y == 1 (mod 2^n), where x is odd.  Return y. */
+  /* Solve x*y == 1 (mod 2^n), where x is odd.  Return y.  */
 
   /* The algorithm notes that the choice y = x satisfies
      x*y == 1 mod 2^3, since x is assumed odd.
@@ -2500,14 +2706,16 @@ expand_mult_highpart_adjust (mode, adj_operand, op0, op1, target, unsignedp)
                      build_int_2 (GET_MODE_BITSIZE (mode) - 1, 0),
                      NULL_RTX, 0);
   tem = expand_and (tem, op1, NULL_RTX);
-  adj_operand = force_operand (gen_rtx (adj_code, mode, adj_operand, tem),
-                              adj_operand);
+  adj_operand
+    = force_operand (gen_rtx_fmt_ee (adj_code, mode, adj_operand, tem),
+                    adj_operand);
 
   tem = expand_shift (RSHIFT_EXPR, mode, op1,
                      build_int_2 (GET_MODE_BITSIZE (mode) - 1, 0),
                      NULL_RTX, 0);
   tem = expand_and (tem, op0, NULL_RTX);
-  target = force_operand (gen_rtx (adj_code, mode, adj_operand, tem), target);
+  target = force_operand (gen_rtx_fmt_ee (adj_code, mode, adj_operand, tem),
+                         target);
 
   return target;
 }
@@ -2599,13 +2807,19 @@ expand_mult_highpart (mode, op0, cnst1, target, unsignedp, max_cost)
   moptab = unsignedp ? umul_widen_optab : smul_widen_optab;
   if (moptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing
       && mul_widen_cost[(int) wider_mode] < max_cost)
-    goto try;
+    {
+      op1 = force_reg (mode, op1);
+      goto try;
+    } 
 
   /* Try widening the mode and perform a non-widening multiplication.  */
   moptab = smul_optab;
   if (smul_optab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing
       && mul_cost[(int) wider_mode] + shift_cost[size-1] < max_cost)
-    goto try;
+    {
+      op1 = wide_op1;
+      goto try;
+    }
 
   /* Try widening multiplication of opposite signedness, and adjust.  */
   moptab = unsignedp ? smul_widen_optab : umul_widen_optab;
@@ -2613,7 +2827,8 @@ expand_mult_highpart (mode, op0, cnst1, target, unsignedp, max_cost)
       && (mul_widen_cost[(int) wider_mode]
          + 2 * shift_cost[size-1] + 4 * add_cost < max_cost))
     {
-      tem = expand_binop (wider_mode, moptab, op0, wide_op1,
+      rtx regop1 = force_reg (mode, op1);
+      tem = expand_binop (wider_mode, moptab, op0, regop1,
                          NULL_RTX, ! unsignedp, OPTAB_WIDEN);
       if (tem != 0)
        {
@@ -2631,15 +2846,22 @@ expand_mult_highpart (mode, op0, cnst1, target, unsignedp, max_cost)
 
  try:
   /* Pass NULL_RTX as target since TARGET has wrong mode.  */
-  tem = expand_binop (wider_mode, moptab, op0, wide_op1,
+  tem = expand_binop (wider_mode, moptab, op0, op1,
                      NULL_RTX, unsignedp, OPTAB_WIDEN);
   if (tem == 0)
     return 0;
 
   /* Extract the high half of the just generated product.  */
-  tem = expand_shift (RSHIFT_EXPR, wider_mode, tem,
-                     build_int_2 (size, 0), NULL_RTX, 1);
-  return convert_modes (mode, wider_mode, tem, unsignedp);
+  if (mode == word_mode)
+    {
+      return gen_highpart (mode, tem);
+    }
+  else
+    {
+      tem = expand_shift (RSHIFT_EXPR, wider_mode, tem,
+                         build_int_2 (size, 0), NULL_RTX, 1);
+      return convert_modes (mode, wider_mode, tem, unsignedp);
+    }
 }
 \f
 /* Emit the code to divide OP0 by OP1, putting the result in TARGET
@@ -2658,6 +2880,27 @@ expand_mult_highpart (mode, op0, cnst1, target, unsignedp, max_cost)
    This could optimize to a bfexts instruction.
    But C doesn't use these operations, so their optimizations are
    left for later.  */
+/* ??? For modulo, we don't actually need the highpart of the first product,
+   the low part will do nicely.  And for small divisors, the second multiply
+   can also be a low-part only multiply or even be completely left out.
+   E.g. to calculate the remainder of a division by 3 with a 32 bit
+   multiply, multiply with 0x55555556 and extract the upper two bits;
+   the result is exact for inputs up to 0x1fffffff.
+   The input range can be reduced by using cross-sum rules.
+   For odd divisors >= 3, the following table gives right shift counts
+   so that if an number is shifted by an integer multiple of the given
+   amount, the remainder stays the same:
+   2, 4, 3, 6, 10, 12, 4, 8, 18, 6, 11, 20, 18, 0, 5, 10, 12, 0, 12, 20,
+   14, 12, 23, 21, 8, 0, 20, 18, 0, 0, 6, 12, 0, 22, 0, 18, 20, 30, 0, 0,
+   0, 8, 0, 11, 12, 10, 36, 0, 30, 0, 0, 12, 0, 0, 0, 0, 44, 12, 24, 0,
+   20, 0, 7, 14, 0, 18, 36, 0, 0, 46, 60, 0, 42, 0, 15, 24, 20, 0, 0, 33,
+   0, 20, 0, 0, 18, 0, 60, 0, 0, 0, 0, 0, 40, 18, 0, 0, 12
+
+   Cross-sum rules for even numbers can be derived by leaving as many bits
+   to the right alone as the divisor has zeros to the right.
+   E.g. if x is an unsigned 32 bit number:
+   (x mod 12) == (((x & 1023) + ((x >> 8) & ~3)) * 0x15555558 >> 2 * 3) >> 28
+   */
 
 #define EXACT_POWER_OF_2_OR_ZERO_P(x) (((x) & ((x) - 1)) == 0)
 
@@ -2678,11 +2921,12 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
   optab optab1, optab2;
   int op1_is_constant, op1_is_pow2;
   int max_cost, extra_cost;
+  static HOST_WIDE_INT last_div_const = 0;
 
   op1_is_constant = GET_CODE (op1) == CONST_INT;
   op1_is_pow2 = (op1_is_constant
                 && ((EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1))
-                     || EXACT_POWER_OF_2_OR_ZERO_P (-INTVAL (op1)))));
+                     || (! unsignedp && EXACT_POWER_OF_2_OR_ZERO_P (-INTVAL (op1))))));
 
   /*
      This is the structure of expand_divmod:
@@ -2787,14 +3031,29 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
   size = GET_MODE_BITSIZE (mode);
 #endif
 
+  /* Only deduct something for a REM if the last divide done was
+     for a different constant.   Then set the constant of the last
+     divide.  */
   max_cost = div_cost[(int) compute_mode]
-    - (rem_flag ? mul_cost[(int) compute_mode] + add_cost : 0);
+    - (rem_flag && ! (last_div_const != 0 && op1_is_constant
+                     && INTVAL (op1) == last_div_const)
+       ? mul_cost[(int) compute_mode] + add_cost : 0);
+
+  last_div_const = ! rem_flag && op1_is_constant ? INTVAL (op1) : 0;
 
   /* Now convert to the best mode to use.  */
   if (compute_mode != mode)
     {
       op0 = convert_modes (compute_mode, mode, op0, unsignedp);
       op1 = convert_modes (compute_mode, mode, op1, unsignedp);
+
+      /* convert_modes may have placed op1 into a register, so we
+        must recompute the following.  */
+      op1_is_constant = GET_CODE (op1) == CONST_INT;
+      op1_is_pow2 = (op1_is_constant
+                    && ((EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1))
+                         || (! unsignedp
+                             && EXACT_POWER_OF_2_OR_ZERO_P (-INTVAL (op1)))))) ;
     }
 
   /* If one of the operands is a volatile MEM, copy it into a register.  */
@@ -2811,13 +3070,15 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
 
   last = get_last_insn ();
 
-  /* Promote floor rouding to trunc rounding for unsigned operations.  */
+  /* Promote floor rounding to trunc rounding for unsigned operations.  */
   if (unsignedp)
     {
       if (code == FLOOR_DIV_EXPR)
        code = TRUNC_DIV_EXPR;
       if (code == FLOOR_MOD_EXPR)
        code = TRUNC_MOD_EXPR;
+      if (code == EXACT_DIV_EXPR && op1_is_pow2)
+       code = TRUNC_DIV_EXPR;
     }
 
   if (op1 != const0_rtx)
@@ -2825,11 +3086,9 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
       {
       case TRUNC_MOD_EXPR:
       case TRUNC_DIV_EXPR:
-       if (op1_is_constant && HOST_BITS_PER_WIDE_INT >= size)
+       if (op1_is_constant)
          {
-           if (unsignedp
-               || (INTVAL (op1)
-                   == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (compute_mode) - 1)))
+           if (unsignedp)
              {
                unsigned HOST_WIDE_INT mh, ml;
                int pre_shift, post_shift;
@@ -2841,10 +3100,11 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                    pre_shift = floor_log2 (d);
                    if (rem_flag)
                      {
-                       remainder = expand_binop (compute_mode, and_optab, op0,
-                                                 GEN_INT (((HOST_WIDE_INT) 1 << pre_shift) - 1),
-                                                 remainder, 1,
-                                                 OPTAB_LIB_WIDEN);
+                       remainder
+                         = expand_binop (compute_mode, and_optab, op0,
+                                         GEN_INT (((HOST_WIDE_INT) 1 << pre_shift) - 1),
+                                         remainder, 1,
+                                         OPTAB_LIB_WIDEN);
                        if (remainder)
                          return gen_lowpart (mode, remainder);
                      }
@@ -2852,92 +3112,95 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                                             build_int_2 (pre_shift, 0),
                                             tquotient, 1);
                  }
-               else if (d >= ((unsigned HOST_WIDE_INT) 1 << (size - 1)))
-                 {
-                   /* Most significant bit of divisor is set, emit a scc insn.
-                      emit_store_flag needs to be passed a place for the
-                      result.  */
-                   quotient = emit_store_flag (tquotient, GEU, op0, op1,
-                                               compute_mode, 1, 1);
-                   /* Can emit_store_flag have failed? */
-                   if (quotient == 0)
-                     goto fail1;
-                 }
-               else
+               else if (size <= HOST_BITS_PER_WIDE_INT)
                  {
-                   /* Find a suitable multiplier and right shift count instead
-                      of multiplying with D.  */
-
-                   mh = choose_multiplier (d, size, size,
-                                           &ml, &post_shift, &dummy);
-
-                   /* If the suggested multiplier is more than SIZE bits, we
-                      can do better for even divisors, using an initial right
-                      shift.  */
-                   if (mh != 0 && (d & 1) == 0)
+                   if (d >= ((unsigned HOST_WIDE_INT) 1 << (size - 1)))
                      {
-                       pre_shift = floor_log2 (d & -d);
-                       mh = choose_multiplier (d >> pre_shift, size,
-                                               size - pre_shift,
-                                               &ml, &post_shift, &dummy);
-                       if (mh)
-                         abort ();
-                     }
-                   else
-                     pre_shift = 0;
-
-                   if (mh != 0)
-                     {
-                       rtx t1, t2, t3, t4;
-
-                       extra_cost = (shift_cost[post_shift - 1]
-                                     + shift_cost[1] + 2 * add_cost);
-                       t1 = expand_mult_highpart (compute_mode, op0, ml,
-                                                  NULL_RTX, 1,
-                                                  max_cost - extra_cost);
-                       if (t1 == 0)
+                       /* Most significant bit of divisor is set; emit an scc
+                          insn.  */
+                       quotient = emit_store_flag (tquotient, GEU, op0, op1,
+                                                   compute_mode, 1, 1);
+                       if (quotient == 0)
                          goto fail1;
-                       t2 = force_operand (gen_rtx (MINUS, compute_mode,
-                                                    op0, t1),
-                                           NULL_RTX);
-                       t3 = expand_shift (RSHIFT_EXPR, compute_mode, t2,
-                                          build_int_2 (1, 0), NULL_RTX, 1);
-                       t4 = force_operand (gen_rtx (PLUS, compute_mode,
-                                                    t1, t3),
-                                           NULL_RTX);
-                       quotient = expand_shift (RSHIFT_EXPR, compute_mode, t4,
-                                                build_int_2 (post_shift - 1,
-                                                             0),
-                                                tquotient, 1);
                      }
                    else
                      {
-                       rtx t1, t2;
+                       /* Find a suitable multiplier and right shift count
+                          instead of multiplying with D.  */
 
-                       t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
-                                          build_int_2 (pre_shift, 0),
-                                          NULL_RTX, 1);
-                       extra_cost = (shift_cost[pre_shift]
-                                     + shift_cost[post_shift]);
-                       t2 = expand_mult_highpart (compute_mode, t1, ml,
-                                                  NULL_RTX, 1,
-                                                  max_cost - extra_cost);
-                       if (t2 == 0)
-                         goto fail1;
-                       quotient = expand_shift (RSHIFT_EXPR, compute_mode, t2,
-                                                build_int_2 (post_shift, 0),
-                                                tquotient, 1);
+                       mh = choose_multiplier (d, size, size,
+                                               &ml, &post_shift, &dummy);
+
+                       /* If the suggested multiplier is more than SIZE bits,
+                          we can do better for even divisors, using an
+                          initial right shift.  */
+                       if (mh != 0 && (d & 1) == 0)
+                         {
+                           pre_shift = floor_log2 (d & -d);
+                           mh = choose_multiplier (d >> pre_shift, size,
+                                                   size - pre_shift,
+                                                   &ml, &post_shift, &dummy);
+                           if (mh)
+                             abort ();
+                         }
+                       else
+                         pre_shift = 0;
+
+                       if (mh != 0)
+                         {
+                           rtx t1, t2, t3, t4;
+
+                           extra_cost = (shift_cost[post_shift - 1]
+                                         + shift_cost[1] + 2 * add_cost);
+                           t1 = expand_mult_highpart (compute_mode, op0, ml,
+                                                      NULL_RTX, 1,
+                                                      max_cost - extra_cost);
+                           if (t1 == 0)
+                             goto fail1;
+                           t2 = force_operand (gen_rtx_MINUS (compute_mode,
+                                                              op0, t1),
+                                               NULL_RTX);
+                           t3 = expand_shift (RSHIFT_EXPR, compute_mode, t2,
+                                              build_int_2 (1, 0), NULL_RTX,1);
+                           t4 = force_operand (gen_rtx_PLUS (compute_mode,
+                                                             t1, t3),
+                                               NULL_RTX);
+                           quotient
+                             = expand_shift (RSHIFT_EXPR, compute_mode, t4,
+                                             build_int_2 (post_shift - 1, 0),
+                                             tquotient, 1);
+                         }
+                       else
+                         {
+                           rtx t1, t2;
+
+                           t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+                                              build_int_2 (pre_shift, 0),
+                                              NULL_RTX, 1);
+                           extra_cost = (shift_cost[pre_shift]
+                                         + shift_cost[post_shift]);
+                           t2 = expand_mult_highpart (compute_mode, t1, ml,
+                                                      NULL_RTX, 1,
+                                                      max_cost - extra_cost);
+                           if (t2 == 0)
+                             goto fail1;
+                           quotient
+                             = expand_shift (RSHIFT_EXPR, compute_mode, t2,
+                                             build_int_2 (post_shift, 0),
+                                             tquotient, 1);
+                         }
                      }
                  }
+               else            /* Too wide mode to use tricky code */
+                 break;
 
                insn = get_last_insn ();
                if (insn != last
                    && (set = single_set (insn)) != 0
                    && SET_DEST (set) == quotient)
-                 REG_NOTES (insn)
-                   = gen_rtx (EXPR_LIST, REG_EQUAL,
-                              gen_rtx (UDIV, compute_mode, op0, op1),
-                              REG_NOTES (insn));
+                 set_unique_reg_note (insn, 
+                                      REG_EQUAL,
+                                      gen_rtx_UDIV (compute_mode, op0, op1));
              }
            else                /* TRUNC_DIV, signed */
              {
@@ -2958,6 +3221,14 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                else if (d == -1)
                  quotient = expand_unop (compute_mode, neg_optab, op0,
                                          tquotient, 0);
+               else if (abs_d == (unsigned HOST_WIDE_INT) 1 << (size - 1))
+                 {
+                   /* This case is not handled correctly below.  */
+                   quotient = emit_store_flag (tquotient, EQ, op0, op1,
+                                               compute_mode, 1, 1);
+                   if (quotient == 0)
+                     goto fail1;
+                 }
                else if (EXACT_POWER_OF_2_OR_ZERO_P (d)
                         && (rem_flag ? smod_pow2_cheap : sdiv_pow2_cheap))
                  ;
@@ -2970,9 +3241,8 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                        rtx t1;
 
                        t1 = copy_to_mode_reg (compute_mode, op0);
-                       emit_cmp_insn (t1, const0_rtx, GE, 
-                                      NULL_RTX, compute_mode, 0, 0);
-                       emit_jump_insn (gen_bge (label));
+                       do_cmp_and_jump (t1, const0_rtx, GE,
+                                        compute_mode, label);
                        expand_inc (t1, GEN_INT (abs_d - 1));
                        emit_label (label);
                        quotient = expand_shift (RSHIFT_EXPR, compute_mode, t1,
@@ -2988,8 +3258,8 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                        t2 = expand_shift (RSHIFT_EXPR, compute_mode, t1,
                                           build_int_2 (size - lgup, 0),
                                           NULL_RTX, 1);
-                       t3 = force_operand (gen_rtx (PLUS, compute_mode,
-                                                    op0, t2),
+                       t3 = force_operand (gen_rtx_PLUS (compute_mode,
+                                                         op0, t2),
                                            NULL_RTX);
                        quotient = expand_shift (RSHIFT_EXPR, compute_mode, t3,
                                                 build_int_2 (lgup, 0),
@@ -3003,18 +3273,20 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                        insn = get_last_insn ();
                        if (insn != last
                            && (set = single_set (insn)) != 0
-                           && SET_DEST (set) == quotient)
-                         REG_NOTES (insn)
-                           = gen_rtx (EXPR_LIST, REG_EQUAL,
-                                      gen_rtx (DIV, compute_mode, op0,
-                                               GEN_INT (abs_d)),
-                                      REG_NOTES (insn));
+                           && SET_DEST (set) == quotient
+                           && abs_d < ((unsigned HOST_WIDE_INT) 1
+                                       << (HOST_BITS_PER_WIDE_INT - 1)))
+                         set_unique_reg_note (insn, 
+                                              REG_EQUAL,
+                                              gen_rtx_DIV (compute_mode,
+                                                           op0,
+                                                           GEN_INT (abs_d)));
 
                        quotient = expand_unop (compute_mode, neg_optab,
                                                quotient, quotient, 0);
                      }
                  }
-               else
+               else if (size <= HOST_BITS_PER_WIDE_INT)
                  {
                    choose_multiplier (abs_d, size, size - 1,
                                       &ml, &post_shift, &lgup);
@@ -3034,11 +3306,15 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                        t3 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
                                           build_int_2 (size - 1, 0), NULL_RTX, 0);
                        if (d < 0)
-                         quotient = force_operand (gen_rtx (MINUS, compute_mode, t3, t2),
-                                                   tquotient);
+                         quotient
+                           = force_operand (gen_rtx_MINUS (compute_mode,
+                                                           t3, t2),
+                                            tquotient);
                        else
-                         quotient = force_operand (gen_rtx (MINUS, compute_mode, t2, t3),
-                                                   tquotient);
+                         quotient
+                           = force_operand (gen_rtx_MINUS (compute_mode,
+                                                           t2, t3),
+                                            tquotient);
                      }
                    else
                      {
@@ -3052,29 +3328,37 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                                                   max_cost - extra_cost);
                        if (t1 == 0)
                          goto fail1;
-                       t2 = force_operand (gen_rtx (PLUS, compute_mode, t1, op0),
+                       t2 = force_operand (gen_rtx_PLUS (compute_mode,
+                                                         t1, op0),
                                            NULL_RTX);
                        t3 = expand_shift (RSHIFT_EXPR, compute_mode, t2,
-                                          build_int_2 (post_shift, 0), NULL_RTX, 0);
+                                          build_int_2 (post_shift, 0),
+                                          NULL_RTX, 0);
                        t4 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
-                                          build_int_2 (size - 1, 0), NULL_RTX, 0);
+                                          build_int_2 (size - 1, 0),
+                                          NULL_RTX, 0);
                        if (d < 0)
-                         quotient = force_operand (gen_rtx (MINUS, compute_mode, t4, t3),
-                                                   tquotient);
+                         quotient
+                           = force_operand (gen_rtx_MINUS (compute_mode,
+                                                           t4, t3),
+                                            tquotient);
                        else
-                         quotient = force_operand (gen_rtx (MINUS, compute_mode, t3, t4),
-                                                   tquotient);
+                         quotient
+                           = force_operand (gen_rtx_MINUS (compute_mode,
+                                                           t3, t4),
+                                            tquotient);
                      }
                  }
+               else            /* Too wide mode to use tricky code */
+                 break;
 
                insn = get_last_insn ();
                if (insn != last
                    && (set = single_set (insn)) != 0
                    && SET_DEST (set) == quotient)
-                 REG_NOTES (insn)
-                   = gen_rtx (EXPR_LIST, REG_EQUAL,
-                              gen_rtx (DIV, compute_mode, op0, op1),
-                              REG_NOTES (insn));
+                 set_unique_reg_note (insn, 
+                                      REG_EQUAL,
+                                      gen_rtx_DIV (compute_mode, op0, op1));
              }
            break;
          }
@@ -3142,13 +3426,13 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
            else
              {
                rtx nsign, t1, t2, t3, t4;
-               t1 = force_operand (gen_rtx (PLUS, compute_mode,
-                                            op0, constm1_rtx), NULL_RTX);
+               t1 = force_operand (gen_rtx_PLUS (compute_mode,
+                                                 op0, constm1_rtx), NULL_RTX);
                t2 = expand_binop (compute_mode, ior_optab, op0, t1, NULL_RTX,
                                   0, OPTAB_WIDEN);
                nsign = expand_shift (RSHIFT_EXPR, compute_mode, t2,
                                      build_int_2 (size - 1, 0), NULL_RTX, 0);
-               t3 = force_operand (gen_rtx (MINUS, compute_mode, t1, nsign),
+               t3 = force_operand (gen_rtx_MINUS (compute_mode, t1, nsign),
                                    NULL_RTX);
                t4 = expand_divmod (0, TRUNC_DIV_EXPR, compute_mode, t3, op1,
                                    NULL_RTX, 0);
@@ -3157,8 +3441,8 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                    rtx t5;
                    t5 = expand_unop (compute_mode, one_cmpl_optab, nsign,
                                      NULL_RTX, 0);
-                   quotient = force_operand (gen_rtx (PLUS, compute_mode,
-                                                      t4, t5),
+                   quotient = force_operand (gen_rtx_PLUS (compute_mode,
+                                                           t4, t5),
                                              tquotient);
                  }
              }
@@ -3173,7 +3457,7 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
           or remainder to get floor rounding, once we have the remainder.
           Notice that we compute also the final remainder value here,
           and return the result right away.  */
-       if (target == 0)
+       if (target == 0 || GET_MODE (target) != compute_mode)
          target = gen_reg_rtx (compute_mode);
 
        if (rem_flag)
@@ -3196,13 +3480,10 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
               Save that for later.  */
            rtx tem;
            rtx label = gen_label_rtx ();
-           emit_cmp_insn (remainder, const0_rtx, EQ, NULL_RTX,
-                          compute_mode, 0, 0);
-           emit_jump_insn (gen_beq (label));
+           do_cmp_and_jump (remainder, const0_rtx, EQ, compute_mode, label);
            tem = expand_binop (compute_mode, xor_optab, op0, op1,
                                NULL_RTX, 0, OPTAB_WIDEN);
-           emit_cmp_insn (tem, const0_rtx, GE, NULL_RTX, compute_mode, 0, 0);
-           emit_jump_insn (gen_bge (label));
+           do_cmp_and_jump (tem, const0_rtx, GE, compute_mode, label);
            expand_dec (quotient, const1_rtx);
            expand_inc (remainder, op1);
            emit_label (label);
@@ -3223,11 +3504,8 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
          label3 = gen_label_rtx ();
          label4 = gen_label_rtx ();
          label5 = gen_label_rtx ();
-         emit_cmp_insn (op1, const0_rtx, LT, NULL_RTX, compute_mode, 0, 0);
-         emit_jump_insn (gen_blt (label2));
-         emit_cmp_insn (adjusted_op0, const0_rtx, LT, NULL_RTX,
-                        compute_mode, 0, 0);
-         emit_jump_insn (gen_blt (label1));
+         do_cmp_and_jump (op1, const0_rtx, LT, compute_mode, label2);
+         do_cmp_and_jump (adjusted_op0, const0_rtx, LT, compute_mode, label1);
          tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
                              quotient, 0, OPTAB_LIB_WIDEN);
          if (tem != quotient)
@@ -3239,9 +3517,7 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
          emit_jump_insn (gen_jump (label4));
          emit_barrier ();
          emit_label (label2);
-         emit_cmp_insn (adjusted_op0, const0_rtx, GT, NULL_RTX,
-                        compute_mode, 0, 0);
-         emit_jump_insn (gen_bgt (label3));
+         do_cmp_and_jump (adjusted_op0, const0_rtx, GT, compute_mode, label3);
          tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
                              quotient, 0, OPTAB_LIB_WIDEN);
          if (tem != quotient)
@@ -3281,16 +3557,14 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                  {
                    rtx lab;
                    lab = gen_label_rtx ();
-                   emit_cmp_insn (t2, const0_rtx, EQ, NULL_RTX,
-                                  compute_mode, 0, 0);
-                   emit_jump_insn (gen_beq (lab));
+                   do_cmp_and_jump (t2, const0_rtx, EQ, compute_mode, lab);
                    expand_inc (t1, const1_rtx);
                    emit_label (lab);
                    quotient = t1;
                  }
                else
-                 quotient = force_operand (gen_rtx (PLUS, compute_mode,
-                                                    t1, t3),
+                 quotient = force_operand (gen_rtx_PLUS (compute_mode,
+                                                         t1, t3),
                                            tquotient);
                break;
              }
@@ -3300,7 +3574,7 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
               quotient or remainder to get ceiling rounding, once we have the
               remainder.  Notice that we compute also the final remainder
               value here, and return the result right away.  */
-           if (target == 0)
+           if (target == 0 || GET_MODE (target) != compute_mode)
              target = gen_reg_rtx (compute_mode);
 
            if (rem_flag)
@@ -3322,9 +3596,8 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                /* This could be computed with a branch-less sequence.
                   Save that for later.  */
                rtx label = gen_label_rtx ();
-               emit_cmp_insn (remainder, const0_rtx, EQ, NULL_RTX,
-                              compute_mode, 0, 0);
-               emit_jump_insn (gen_beq (label));
+               do_cmp_and_jump (remainder, const0_rtx, EQ,
+                                compute_mode, label);
                expand_inc (quotient, const1_rtx);
                expand_dec (remainder, op1);
                emit_label (label);
@@ -3341,9 +3614,8 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
              adjusted_op0 = copy_to_mode_reg (compute_mode, op0);
              label1 = gen_label_rtx ();
              label2 = gen_label_rtx ();
-             emit_cmp_insn (adjusted_op0, const0_rtx, NE, NULL_RTX,
-                            compute_mode, 0, 0);
-             emit_jump_insn (gen_bne (label1));
+             do_cmp_and_jump (adjusted_op0, const0_rtx, NE,
+                              compute_mode, label1);
              emit_move_insn  (quotient, const0_rtx);
              emit_jump_insn (gen_jump (label2));
              emit_barrier ();
@@ -3383,16 +3655,14 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                  {
                    rtx lab;
                    lab = gen_label_rtx ();
-                   emit_cmp_insn (t2, const0_rtx, EQ, NULL_RTX,
-                                  compute_mode, 0, 0);
-                   emit_jump_insn (gen_beq (lab));
+                   do_cmp_and_jump (t2, const0_rtx, EQ, compute_mode, lab);
                    expand_inc (t1, const1_rtx);
                    emit_label (lab);
                    quotient = t1;
                  }
                else
-                 quotient = force_operand (gen_rtx (PLUS, compute_mode,
-                                                    t1, t3),
+                 quotient = force_operand (gen_rtx_PLUS (compute_mode,
+                                                         t1, t3),
                                            tquotient);
                break;
              }
@@ -3402,7 +3672,7 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
               quotient or remainder to get ceiling rounding, once we have the
               remainder.  Notice that we compute also the final remainder
               value here, and return the result right away.  */
-           if (target == 0)
+           if (target == 0 || GET_MODE (target) != compute_mode)
              target = gen_reg_rtx (compute_mode);
            if (rem_flag)
              {
@@ -3424,14 +3694,11 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                   Save that for later.  */
                rtx tem;
                rtx label = gen_label_rtx ();
-               emit_cmp_insn (remainder, const0_rtx, EQ, NULL_RTX,
-                              compute_mode, 0, 0);
-               emit_jump_insn (gen_beq (label));
+               do_cmp_and_jump (remainder, const0_rtx, EQ,
+                                compute_mode, label);
                tem = expand_binop (compute_mode, xor_optab, op0, op1,
                                    NULL_RTX, 0, OPTAB_WIDEN);
-               emit_cmp_insn (tem, const0_rtx, LT, NULL_RTX,
-                              compute_mode, 0, 0);
-               emit_jump_insn (gen_blt (label));
+               do_cmp_and_jump (tem, const0_rtx, LT, compute_mode, label);
                expand_inc (quotient, const1_rtx);
                expand_dec (remainder, op1);
                emit_label (label);
@@ -3452,12 +3719,9 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
              label3 = gen_label_rtx ();
              label4 = gen_label_rtx ();
              label5 = gen_label_rtx ();
-             emit_cmp_insn (op1, const0_rtx, LT, NULL_RTX,
-                            compute_mode, 0, 0);
-             emit_jump_insn (gen_blt (label2));
-             emit_cmp_insn (adjusted_op0, const0_rtx, GT, NULL_RTX,
-                            compute_mode, 0, 0);
-             emit_jump_insn (gen_bgt (label1));
+             do_cmp_and_jump (op1, const0_rtx, LT, compute_mode, label2);
+             do_cmp_and_jump (adjusted_op0, const0_rtx, GT,
+                              compute_mode, label1);
              tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
                                  quotient, 0, OPTAB_LIB_WIDEN);
              if (tem != quotient)
@@ -3469,9 +3733,8 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
              emit_jump_insn (gen_jump (label4));
              emit_barrier ();
              emit_label (label2);
-             emit_cmp_insn (adjusted_op0, const0_rtx, LT, NULL_RTX,
-                            compute_mode, 0, 0);
-             emit_jump_insn (gen_blt (label3));
+             do_cmp_and_jump (adjusted_op0, const0_rtx, LT,
+                              compute_mode, label3);
              tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
                                  quotient, 0, OPTAB_LIB_WIDEN);
              if (tem != quotient)
@@ -3508,11 +3771,11 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                                     NULL_RTX, unsignedp);
 
            insn = get_last_insn ();
-           REG_NOTES (insn)
-             = gen_rtx (EXPR_LIST, REG_EQUAL,
-                        gen_rtx (unsignedp ? UDIV : DIV, compute_mode,
-                                 op0, op1),
-                        REG_NOTES (insn));
+           set_unique_reg_note (insn,
+                                REG_EQUAL,
+                                gen_rtx_fmt_ee (unsignedp ? UDIV : DIV,
+                                                compute_mode,
+                                                op0, op1));
          }
        break;
 
@@ -3537,8 +3800,7 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
            tem = plus_constant (op1, -1);
            tem = expand_shift (RSHIFT_EXPR, compute_mode, tem,
                                build_int_2 (1, 0), NULL_RTX, 1);
-           emit_cmp_insn (remainder, tem, LEU, NULL_RTX, compute_mode, 0, 0);
-           emit_jump_insn (gen_bleu (label));
+           do_cmp_and_jump (remainder, tem, LEU, compute_mode, label);
            expand_inc (quotient, const1_rtx);
            expand_dec (remainder, op1);
            emit_label (label);
@@ -3559,12 +3821,11 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                remainder = expand_binop (compute_mode, sub_optab, op0, tem,
                                          remainder, 0, OPTAB_LIB_WIDEN);
              }
-           abs_rem = expand_abs (compute_mode, remainder, NULL_RTX, 0, 0);
-           abs_op1 = expand_abs (compute_mode, op1, NULL_RTX, 0, 0);
+           abs_rem = expand_abs (compute_mode, remainder, NULL_RTX, 0);
+           abs_op1 = expand_abs (compute_mode, op1, NULL_RTX, 0);
            tem = expand_shift (LSHIFT_EXPR, compute_mode, abs_rem,
                                build_int_2 (1, 0), NULL_RTX, 1);
-           emit_cmp_insn (tem, abs_op1, LTU, NULL_RTX, compute_mode, 0, 0);
-           emit_jump_insn (gen_bltu (label));
+           do_cmp_and_jump (tem, abs_op1, LTU, compute_mode, label);
            tem = expand_binop (compute_mode, xor_optab, op0, op1,
                                NULL_RTX, 0, OPTAB_WIDEN);
            mask = expand_shift (RSHIFT_EXPR, compute_mode, tem,
@@ -3582,16 +3843,34 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
            emit_label (label);
          }
        return gen_lowpart (mode, rem_flag ? remainder : quotient);
+       
+      default:
+       abort ();
       }
 
   if (quotient == 0)
     {
+      if (target && GET_MODE (target) != compute_mode)
+       target = 0;
+
       if (rem_flag)
        {
-         /* Try to produce the remainder directly without a library call.  */
-         remainder = sign_expand_binop (compute_mode, umod_optab, smod_optab,
-                                        op0, op1, target,
-                                        unsignedp, OPTAB_WIDEN);
+         /* Try to produce the remainder without producing the quotient.
+            If we seem to have a divmod patten that does not require widening,
+            don't try windening here.  We should really have an WIDEN argument
+            to expand_twoval_binop, since what we'd really like to do here is
+            1) try a mod insn in compute_mode
+            2) try a divmod insn in compute_mode
+            3) try a div insn in compute_mode and multiply-subtract to get
+               remainder
+            4) try the same things with widening allowed.  */
+         remainder
+           = sign_expand_binop (compute_mode, umod_optab, smod_optab,
+                                op0, op1, target,
+                                unsignedp,
+                                ((optab2->handlers[(int) compute_mode].insn_code
+                                  != CODE_FOR_nothing)
+                                 ? OPTAB_DIRECT : OPTAB_WIDEN));
          if (remainder == 0)
            {
              /* No luck there.  Can we do remainder and divide at once
@@ -3609,11 +3888,18 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
            return gen_lowpart (mode, remainder);
        }
 
-      /* Produce the quotient.  */
-      /* Try a quotient insn, but not a library call.  */
-      quotient = sign_expand_binop (compute_mode, udiv_optab, sdiv_optab,
-                                   op0, op1, rem_flag ? NULL_RTX : target,
-                                   unsignedp, OPTAB_WIDEN);
+      /* Produce the quotient.  Try a quotient insn, but not a library call.
+        If we have a divmod in this mode, use it in preference to widening
+        the div (for this test we assume it will not fail). Note that optab2
+        is set to the one of the two optabs that the call below will use.  */
+      quotient
+       = sign_expand_binop (compute_mode, udiv_optab, sdiv_optab,
+                            op0, op1, rem_flag ? NULL_RTX : target,
+                            unsignedp,
+                            ((optab2->handlers[(int) compute_mode].insn_code
+                              != CODE_FOR_nothing)
+                             ? OPTAB_DIRECT : OPTAB_WIDEN));
+
       if (quotient == 0)
        {
          /* No luck there.  Try a quotient-and-remainder insn,
@@ -3637,6 +3923,9 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
 
   if (rem_flag)
     {
+      if (target && GET_MODE (target) != compute_mode)
+       target = 0;
+
       if (quotient == 0)
        /* No divide instruction either.  Use library for remainder.  */
        remainder = sign_expand_binop (compute_mode, umod_optab, smod_optab,
@@ -3672,7 +3961,9 @@ make_tree (type, x)
     {
     case CONST_INT:
       t = build_int_2 (INTVAL (x),
-                      TREE_UNSIGNED (type) || INTVAL (x) >= 0 ? 0 : -1);
+                      (TREE_UNSIGNED (type)
+                       && (GET_MODE_BITSIZE (TYPE_MODE (type)) < HOST_BITS_PER_WIDE_INT))
+                      || INTVAL (x) >= 0 ? 0 : -1);
       TREE_TYPE (t) = type;
       return t;
 
@@ -3819,7 +4110,7 @@ expand_and (op0, op1, target)
    to perform the operation.  It says to use zero-extension.
 
    NORMALIZEP is 1 if we should convert the result to be either zero
-   or one one.  Normalize is -1 if we should convert the result to be
+   or one.  Normalize is -1 if we should convert the result to be
    either zero or -1.  If NORMALIZEP is zero, the result will be left
    "raw" out of the scc insn.  */
 
@@ -3837,9 +4128,12 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
   enum machine_mode compare_mode;
   enum machine_mode target_mode = GET_MODE (target);
   rtx tem;
-  rtx last = 0;
+  rtx last = get_last_insn ();
   rtx pattern, comparison;
 
+  if (unsignedp)
+    code = unsigned_condition (code);
+
   /* If one operand is constant, make it the second one.  Only do this
      if the other operand is not constant as well.  */
 
@@ -3857,7 +4151,7 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
 
   /* For some comparisons with 1 and -1, we can convert this to 
      comparisons with zero.  This will often produce more opportunities for
-     store-flag insns. */
+     store-flag insns.  */
 
   switch (code)
     {
@@ -3885,6 +4179,31 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
       if (op1 == const1_rtx)
        op1 = const0_rtx, code = EQ;
       break;
+    default:
+      break;
+    }
+
+  /* If we are comparing a double-word integer with zero, we can convert
+     the comparison into one involving a single word.  */
+  if (GET_MODE_BITSIZE (mode) == BITS_PER_WORD * 2
+      && GET_MODE_CLASS (mode) == MODE_INT
+      && op1 == const0_rtx)
+    {
+      if (code == EQ || code == NE)
+       {
+         /* Do a logical OR of the two words and compare the result.  */
+         rtx op0h = gen_highpart (word_mode, op0);
+         rtx op0l = gen_lowpart (word_mode, op0);
+         rtx op0both = expand_binop (word_mode, ior_optab, op0h, op0l,
+                                     NULL_RTX, unsignedp, OPTAB_DIRECT);
+         if (op0both != 0)
+           return emit_store_flag (target, code, op0both, op1, word_mode,
+                                   unsignedp, normalizep);
+       }
+      else if (code == LT || code == GE)
+       /* If testing the sign bit, can just test on high word.  */
+       return emit_store_flag (target, code, gen_highpart (word_mode, op0),
+                               op1, word_mode, unsignedp, normalizep);
     }
 
   /* From now on, we won't change CODE, so set ICODE now.  */
@@ -3896,7 +4215,7 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
       && GET_MODE_CLASS (mode) == MODE_INT
       && (normalizep || STORE_FLAG_VALUE == 1
          || (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
-             && (STORE_FLAG_VALUE 
+             && ((STORE_FLAG_VALUE & GET_MODE_MASK (mode))
                  == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)))))
     {
       subtarget = target;
@@ -3915,9 +4234,11 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
        subtarget = 0;
 
       if (code == GE)
-       op0 = expand_unop (mode, one_cmpl_optab, op0, subtarget, 0);
+       op0 = expand_unop (mode, one_cmpl_optab, op0,
+                          ((STORE_FLAG_VALUE == 1 || normalizep)
+                           ? 0 : subtarget), 0);
 
-      if (normalizep || STORE_FLAG_VALUE == 1)
+      if (STORE_FLAG_VALUE == 1 || normalizep)
        /* If we are supposed to produce a 0/1 value, we want to do
           a logical shift from the sign bit to the low-order bit; for
           a -1/0 value, we do an arithmetic shift.  */
@@ -3933,13 +4254,17 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
 
   if (icode != CODE_FOR_nothing)
     {
+      insn_operand_predicate_fn pred;
+
       /* We think we may be able to do this with a scc insn.  Emit the
         comparison and then the scc insn.
 
         compare_from_rtx may call emit_queue, which would be deleted below
-        if the scc insn fails.  So call it ourselves before setting LAST.  */
+        if the scc insn fails.  So call it ourselves before setting LAST.
+        Likewise for do_pending_stack_adjust.  */
 
       emit_queue ();
+      do_pending_stack_adjust ();
       last = get_last_insn ();
 
       comparison
@@ -3958,10 +4283,11 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
        abort ();
 
       /* Get a reference to the target in the proper mode for this insn.  */
-      compare_mode = insn_operand_mode[(int) icode][0];
+      compare_mode = insn_data[(int) icode].operand[0].mode;
       subtarget = target;
+      pred = insn_data[(int) icode].operand[0].predicate;
       if (preserve_subexpressions_p ()
-         || ! (*insn_operand_predicate[(int) icode][0]) (subtarget, compare_mode))
+         || ! (*pred) (subtarget, compare_mode))
        subtarget = gen_reg_rtx (compare_mode);
 
       pattern = GEN_FCN (icode) (subtarget);
@@ -4001,7 +4327,9 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
             we don't have to do anything.  */
          if (normalizep == 0 || normalizep == STORE_FLAG_VALUE)
            ;
-         else if (normalizep == - STORE_FLAG_VALUE)
+         /* STORE_FLAG_VALUE might be the most negative number, so write
+            the comparison this way to avoid a compiler-time warning.  */
+         else if (- normalizep == STORE_FLAG_VALUE)
            op0 = expand_unop (compare_mode, neg_optab, op0, subtarget, 0);
 
          /* We don't want to use STORE_FLAG_VALUE < 0 below since this
@@ -4035,8 +4363,7 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
        }
     }
 
-  if (last)
-    delete_insns_since (last);
+  delete_insns_since (last);
 
   /* If expensive optimizations, use different pseudo registers for each
      insn, instead of reusing the same pseudo.  This leads to better CSE,
@@ -4089,8 +4416,8 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
        normalizep = STORE_FLAG_VALUE;
 
       else if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
-              && (STORE_FLAG_VALUE
-                  == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)))
+              && ((STORE_FLAG_VALUE & GET_MODE_MASK (mode))
+                  == (unsigned HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)))
        ;
       else
        return 0;
@@ -4207,9 +4534,112 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
 
   return tem;
 }
-  emit_jump_insn ((*bcc_gen_fctn[(int) code]) (label));
+
+/* Like emit_store_flag, but always succeeds.  */
+
+rtx
+emit_store_flag_force (target, code, op0, op1, mode, unsignedp, normalizep)
+     rtx target;
+     enum rtx_code code;
+     rtx op0, op1;
+     enum machine_mode mode;
+     int unsignedp;
+     int normalizep;
+{
+  rtx tem, label;
+
+  /* First see if emit_store_flag can do the job.  */
+  tem = emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep);
+  if (tem != 0)
+    return tem;
+
+  if (normalizep == 0)
+    normalizep = 1;
+
+  /* If this failed, we have to do this with set/compare/jump/set code.  */
+
+  if (GET_CODE (target) != REG
+      || reg_mentioned_p (target, op0) || reg_mentioned_p (target, op1))
+    target = gen_reg_rtx (GET_MODE (target));
+
   emit_move_insn (target, const1_rtx);
+  label = gen_label_rtx ();
+  do_compare_rtx_and_jump (op0, op1, code, unsignedp, mode, NULL_RTX, 0,
+                          NULL_RTX, label);
+
+  emit_move_insn (target, const0_rtx);
   emit_label (label);
 
   return target;
 }
+\f
+/* Perform possibly multi-word comparison and conditional jump to LABEL
+   if ARG1 OP ARG2 true where ARG1 and ARG2 are of mode MODE
+
+   The algorithm is based on the code in expr.c:do_jump.
+
+   Note that this does not perform a general comparison.  Only variants
+   generated within expmed.c are correctly handled, others abort (but could
+   be handled if needed).  */
+
+static void
+do_cmp_and_jump (arg1, arg2, op, mode, label)
+     rtx arg1, arg2, label;
+     enum rtx_code op;
+     enum machine_mode mode;
+{
+  /* If this mode is an integer too wide to compare properly,
+     compare word by word.  Rely on cse to optimize constant cases.  */
+
+  if (GET_MODE_CLASS (mode) == MODE_INT
+      && ! can_compare_p (op, mode, ccp_jump))
+    {
+      rtx label2 = gen_label_rtx ();
+
+      switch (op)
+       {
+       case LTU:
+         do_jump_by_parts_greater_rtx (mode, 1, arg2, arg1, label2, label);
+         break;
+
+       case LEU:
+         do_jump_by_parts_greater_rtx (mode, 1, arg1, arg2, label, label2);
+         break;
+
+       case LT:
+         do_jump_by_parts_greater_rtx (mode, 0, arg2, arg1, label2, label);
+         break;
+
+       case GT:
+         do_jump_by_parts_greater_rtx (mode, 0, arg1, arg2, label2, label);
+         break;
+
+       case GE:
+         do_jump_by_parts_greater_rtx (mode, 0, arg2, arg1, label, label2);
+         break;
+
+         /* do_jump_by_parts_equality_rtx compares with zero.  Luckily
+            that's the only equality operations we do */
+       case EQ:
+         if (arg2 != const0_rtx || mode != GET_MODE(arg1))
+           abort();
+         do_jump_by_parts_equality_rtx (arg1, label2, label);
+         break;
+
+       case NE:
+         if (arg2 != const0_rtx || mode != GET_MODE(arg1))
+           abort();
+         do_jump_by_parts_equality_rtx (arg1, label, label2);
+         break;
+
+       default:
+         abort();
+       }
+
+      emit_label (label2);
+    }
+  else
+    {
+      emit_cmp_and_jump_insns (arg1, arg2, op, NULL_RTX, mode, 0, 0, label);
+    }
+}