OSDN Git Service

2000-07-21 Alexandre Petit-Bianco <apbianco@cygnus.com>
[pf3gnuchains/gcc-fork.git] / gcc / expmed.c
index 70c0044..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-97, 1998 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.
 
@@ -22,8 +23,10 @@ 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"
@@ -32,19 +35,27 @@ Boston, MA 02111-1307, 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));
-static void do_cmp_and_jump            PROTO((rtx, rtx, enum rtx_code,
-                                              enum machine_mode, rtx));
-
-#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. 
@@ -54,7 +65,7 @@ static void do_cmp_and_jump           PROTO((rtx, rtx, enum rtx_code,
 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,
@@ -65,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];
@@ -79,7 +90,6 @@ 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);
@@ -90,11 +100,7 @@ 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);
@@ -122,7 +128,7 @@ init_expmed ()
   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;
 
@@ -168,19 +174,18 @@ init_expmed ()
          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)))),
+                        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.
@@ -204,31 +209,41 @@ negate_rtx (mode, x)
    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 ();
@@ -252,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.  */
@@ -265,19 +295,33 @@ 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);
          else
@@ -294,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);
@@ -306,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;
     }
 
@@ -329,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
@@ -345,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,
@@ -367,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
@@ -399,21 +470,22 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
       && 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, copy OP0
@@ -421,7 +493,7 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
       /* 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
-         && ! ((*insn_operand_predicate[(int) CODE_FOR_insv][0])
+         && ! ((*insn_data[(int) CODE_FOR_insv].operand[0].predicate)
                (op0, VOIDmode)))
        {
          rtx tempreg;
@@ -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);
@@ -502,7 +576,7 @@ 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 = copy_to_reg (value1);
                  value1 = gen_rtx_SUBREG (maxmode, value1, 0);
                }
              else
@@ -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,25 +621,25 @@ 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)
-    struct_align = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
+  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
@@ -593,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)
@@ -609,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)
        {
@@ -717,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.  */
@@ -725,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
@@ -759,10 +833,10 @@ store_split_bit_field (op0, bitsize, bitpos, value, align)
 
   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;
@@ -804,8 +878,7 @@ store_split_bit_field (op0, bitsize, bitpos, value, align)
                 GET_MODE (value) == VOIDmode
                 ? UNITS_PER_WORD
                 : (GET_MODE (value) == BLKmode
-                   ? 1
-                   : GET_MODE_ALIGNMENT (GET_MODE (value)) / BITS_PER_UNIT));
+                   ? 1 : GET_MODE_ALIGNMENT (GET_MODE (value))));
        }
       else
        {
@@ -821,8 +894,7 @@ store_split_bit_field (op0, bitsize, bitpos, value, align)
                 GET_MODE (value) == VOIDmode
                 ? UNITS_PER_WORD
                 : (GET_MODE (value) == BLKmode
-                   ? 1
-                   : GET_MODE_ALIGNMENT (GET_MODE (value)) / BITS_PER_UNIT));
+                   ? 1 : GET_MODE_ALIGNMENT (GET_MODE (value))));
        }
 
       /* If OP0 is a register, then handle OFFSET here.
@@ -866,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.
 
@@ -879,20 +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
+
+#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.  */
@@ -924,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.  */
@@ -942,25 +1053,42 @@ 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);
          else
@@ -981,8 +1109,8 @@ 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);
@@ -995,20 +1123,21 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
          /* 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 ();
@@ -1023,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++)
@@ -1045,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
@@ -1072,13 +1214,11 @@ 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 xop0 = op0;
@@ -1086,8 +1226,11 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
          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)
            {
@@ -1095,7 +1238,7 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
              volatile_ok = 1;
 
              /* Is the memory operand acceptable?  */
-             if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][1])
+             if (! ((*insn_data[(int) CODE_FOR_extzv].operand[1].predicate)
                     (xop0, GET_MODE (xop0))))
                {
                  /* No, load into a reg and extract from there.  */
@@ -1110,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,
@@ -1179,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);
 
@@ -1198,25 +1341,23 @@ 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;
@@ -1225,13 +1366,16 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
          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.  */
@@ -1246,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,
@@ -1314,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);
 
@@ -1333,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;
@@ -1383,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)
@@ -1410,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)
@@ -1424,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)
        {
@@ -1604,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
@@ -1622,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;
@@ -1763,7 +1909,8 @@ expand_shift (code, mode, shifted, amount, target, unsignedp)
   if (SHIFT_COUNT_TRUNCATED)
     {
       if (GET_CODE (op1) == CONST_INT
-          && (unsigned HOST_WIDE_INT) INTVAL (op1) >= GET_MODE_BITSIZE (mode))
+          && ((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
@@ -1916,15 +2063,15 @@ struct algorithm
   char log[MAX_BITS_PER_WORD];
 };
 
-static void synth_mult                 PROTO((struct algorithm *,
-                                              unsigned HOST_WIDE_INT,
-                                              int));
-static unsigned HOST_WIDE_INT choose_multiplier PROTO((unsigned HOST_WIDE_INT,
-                                                      int, int,
-                                                      unsigned HOST_WIDE_INT *,
-                                                      int *, int *));
-static unsigned HOST_WIDE_INT invert_mod2n     PROTO((unsigned HOST_WIDE_INT,
-                                                      int));
+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
@@ -1938,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
@@ -2277,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])
@@ -2292,7 +2440,8 @@ expand_mult (mode, op0, op1, target, unsignedp)
                  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);
+                                        add_target
+                                        ? add_target : accum_target);
                  val_so_far += (HOST_WIDE_INT) 1 << log;
                  break;
 
@@ -2300,7 +2449,8 @@ expand_mult (mode, op0, op1, target, unsignedp)
                  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);
+                                        add_target
+                                        ? add_target : accum_target);
                  val_so_far -= (HOST_WIDE_INT) 1 << log;
                  break;
 
@@ -2309,7 +2459,8 @@ expand_mult (mode, op0, op1, target, unsignedp)
                                        build_int_2 (log, 0), shift_subtarget,
                                        0);
                  accum = force_operand (gen_rtx_PLUS (mode, accum, op0),
-                                        add_target ? add_target : accum_target);
+                                        add_target
+                                        ? add_target : accum_target);
                  val_so_far = (val_so_far << log) + 1;
                  break;
 
@@ -2318,7 +2469,8 @@ expand_mult (mode, op0, op1, target, unsignedp)
                                        build_int_2 (log, 0), shift_subtarget,
                                        0);
                  accum = force_operand (gen_rtx_MINUS (mode, accum, op0),
-                                        add_target ? add_target : accum_target);
+                                        add_target
+                                        ? add_target : accum_target);
                  val_so_far = (val_so_far << log) - 1;
                  break;
 
@@ -2326,7 +2478,8 @@ expand_mult (mode, op0, op1, target, unsignedp)
                  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);
+                                        add_target
+                                        ? add_target : accum_target);
                  val_so_far += val_so_far << log;
                  break;
 
@@ -2340,17 +2493,17 @@ expand_mult (mode, op0, op1, target, unsignedp)
                  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)
@@ -2415,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);
@@ -2440,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
@@ -2453,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,
@@ -2631,7 +2785,7 @@ expand_mult_highpart (mode, op0, cnst1, target, unsignedp, max_cost)
     {
       mul_highpart_optab = unsignedp ? umul_highpart_optab : smul_highpart_optab;
       target = expand_binop (mode, mul_highpart_optab,
-                            op0, wide_op1, target, unsignedp, OPTAB_DIRECT);
+                            op0, op1, target, unsignedp, OPTAB_DIRECT);
       if (target)
        return target;
     }
@@ -2642,7 +2796,7 @@ expand_mult_highpart (mode, op0, cnst1, target, unsignedp, max_cost)
     {
       mul_highpart_optab = unsignedp ? smul_highpart_optab : umul_highpart_optab;
       target = expand_binop (mode, mul_highpart_optab,
-                            op0, wide_op1, target, unsignedp, OPTAB_DIRECT);
+                            op0, op1, target, unsignedp, OPTAB_DIRECT);
       if (target)
        /* We used the wrong signedness.  Adjust the result.  */
        return expand_mult_highpart_adjust (mode, target, op0,
@@ -2726,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)
 
@@ -2746,6 +2921,7 @@ 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
@@ -2855,8 +3031,15 @@ 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)
@@ -3015,10 +3198,9 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                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 */
              {
@@ -3091,13 +3273,14 @@ 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);
@@ -3123,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
                      {
@@ -3141,18 +3328,25 @@ 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 */
@@ -3162,10 +3356,9 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                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;
          }
@@ -3578,12 +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_fmt_ee (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;
 
@@ -3629,8 +3821,8 @@ 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);
            do_cmp_and_jump (tem, abs_op1, LTU, compute_mode, label);
@@ -3663,10 +3855,22 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
 
       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
@@ -3757,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;
 
@@ -3925,6 +4131,9 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
   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.  */
 
@@ -3974,6 +4183,29 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
       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.  */
   icode = setcc_gen_code[(int) code];
 
@@ -4022,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
@@ -4047,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);
@@ -4090,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
@@ -4178,7 +4417,7 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
 
       else if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
               && ((STORE_FLAG_VALUE & GET_MODE_MASK (mode))
-                  == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)))
+                  == (unsigned HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)))
        ;
       else
        return 0;
@@ -4324,15 +4563,10 @@ emit_store_flag_force (target, code, op0, op1, mode, unsignedp, normalizep)
     target = gen_reg_rtx (GET_MODE (target));
 
   emit_move_insn (target, const1_rtx);
-  tem = compare_from_rtx (op0, op1, code, unsignedp, mode, NULL_RTX, 0);
-  if (GET_CODE (tem) == CONST_INT)
-    return tem;
-
   label = gen_label_rtx ();
-  if (bcc_gen_fctn[(int) code] == 0)
-    abort ();
+  do_compare_rtx_and_jump (op0, op1, code, unsignedp, mode, NULL_RTX, 0,
+                          NULL_RTX, label);
 
-  emit_jump_insn ((*bcc_gen_fctn[(int) code]) (label));
   emit_move_insn (target, const0_rtx);
   emit_label (label);
 
@@ -4351,13 +4585,14 @@ emit_store_flag_force (target, code, op0, op1, mode, unsignedp, normalizep)
 static void
 do_cmp_and_jump (arg1, arg2, op, mode, label)
      rtx arg1, arg2, label;
-    enum rtx_code op;
-    enum machine_mode mode;
+     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 (mode))
+  if (GET_MODE_CLASS (mode) == MODE_INT
+      && ! can_compare_p (op, mode, ccp_jump))
     {
       rtx label2 = gen_label_rtx ();
 
@@ -4405,9 +4640,6 @@ do_cmp_and_jump (arg1, arg2, op, mode, label)
     }
   else
     {
-      emit_cmp_insn(arg1, arg2, op, NULL_RTX, mode, 0, 0);
-      if (bcc_gen_fctn[(int) op] == 0)
-       abort ();
-      emit_jump_insn ((*bcc_gen_fctn[(int) op]) (label));
+      emit_cmp_and_jump_insns (arg1, arg2, op, NULL_RTX, mode, 0, 0, label);
     }
 }