OSDN Git Service

2000-07-21 Alexandre Petit-Bianco <apbianco@cygnus.com>
[pf3gnuchains/gcc-fork.git] / gcc / expmed.c
index c0fa722..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.
 
@@ -34,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. 
@@ -56,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,
@@ -81,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);
@@ -92,11 +100,6 @@ 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);
@@ -182,9 +185,7 @@ init_expmed ()
        }
     }
 
-  /* Free the objects we just allocated.  */
   end_sequence ();
-  obfree (free_point);
 }
 
 /* Return an rtx representing minus the value of X.
@@ -208,7 +209,7 @@ 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
@@ -222,19 +223,20 @@ negate_rtx (mode, x)
 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
-  int insv_bitsize;
+  unsigned HOST_WIDE_INT insv_bitsize;
   enum machine_mode op_mode;
 
   op_mode = insn_data[(int) CODE_FOR_insv].operand[3].mode;
@@ -293,16 +295,18 @@ 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)
        {
@@ -334,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);
@@ -346,30 +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_data[icode].operand[1].predicate) (value, fieldmode))
-           value = copy_to_mode_reg (fieldmode, 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 ();
+       }
 
-         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 ();
-           }
+      emit_insn (GEN_FCN (icode)
+                (gen_rtx_SUBREG (fieldmode, op0, offset), value));
 
-         emit_insn (GEN_FCN (icode)
-                    (gen_rtx_SUBREG (fieldmode, op0, offset), value));
-       }
       return value;
     }
 
@@ -383,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
@@ -399,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,
@@ -505,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.  */
@@ -522,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);
@@ -617,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
@@ -663,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)
@@ -787,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.  */
@@ -795,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
@@ -829,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;
@@ -874,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
        {
@@ -891,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.
@@ -936,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.
 
@@ -949,26 +951,28 @@ 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
-  int extv_bitsize;
+  unsigned HOST_WIDE_INT extv_bitsize;
   enum machine_mode extv_mode;
 #endif
 #ifdef HAVE_extzv
-  int extzv_bitsize;
+  unsigned HOST_WIDE_INT extzv_bitsize;
   enum machine_mode extzv_mode;
 #endif
 
@@ -1053,9 +1057,9 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
        && 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
@@ -1068,7 +1072,8 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
                  : 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))
        {
@@ -1104,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);
@@ -1118,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 ();
@@ -1146,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++)
@@ -1168,10 +1174,19 @@ 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.  */
@@ -1203,7 +1218,7 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
          && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
                && (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;
@@ -1238,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,
@@ -1326,15 +1341,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, 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
     {
@@ -1375,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,
@@ -1462,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;
@@ -1512,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)
@@ -1539,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)
@@ -1733,16 +1748,18 @@ 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;
+  unsigned int unit;
+  unsigned int bitsdone = 0;
   rtx result = NULL_RTX;
   int first = 1;
 
@@ -1751,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;
@@ -2046,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
@@ -2551,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);
@@ -2576,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
@@ -2589,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,
@@ -2767,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;
     }
@@ -2778,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,
@@ -4165,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];
 
@@ -4219,9 +4260,11 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
         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
@@ -4548,7 +4591,8 @@ do_cmp_and_jump (arg1, arg2, op, mode, label)
   /* 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 ();