OSDN Git Service

PR c/10175
[pf3gnuchains/gcc-fork.git] / gcc / expmed.c
index 7c1e0f6..33d8152 100644 (file)
@@ -1,7 +1,7 @@
 /* Medium-level subroutines: convert bit-field store and extract
    and shifts, multiplies and divides to rtl instructions.
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -23,6 +23,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.h"
 #include "toplev.h"
 #include "rtl.h"
 #include "tree.h"
@@ -33,31 +35,29 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "optabs.h"
 #include "real.h"
 #include "recog.h"
+#include "langhooks.h"
 
 static void store_fixed_bit_field      PARAMS ((rtx, unsigned HOST_WIDE_INT,
                                                 unsigned HOST_WIDE_INT,
-                                                unsigned HOST_WIDE_INT, rtx,
-                                                unsigned int));
+                                                unsigned HOST_WIDE_INT, rtx));
 static void store_split_bit_field      PARAMS ((rtx, unsigned HOST_WIDE_INT,
-                                                unsigned HOST_WIDE_INT, rtx,
-                                                unsigned int));
+                                                unsigned HOST_WIDE_INT, rtx));
 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));
+                                                rtx, 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));
+                                                unsigned HOST_WIDE_INT, 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. 
+/* Nonzero means divides or modulus operations are relatively cheap for
+   powers of two, so don't use branches; emit the operation instead.
    Usually, this will mean that the MD file will emit non-branch
    sequences.  */
 
@@ -77,14 +77,17 @@ static int sdiv_pow2_cheap, smod_pow2_cheap;
 
 /* Reduce conditional compilation elsewhere.  */
 #ifndef HAVE_insv
+#define HAVE_insv      0
 #define CODE_FOR_insv  CODE_FOR_nothing
 #define gen_insv(a,b,c,d) NULL_RTX
 #endif
 #ifndef HAVE_extv
+#define HAVE_extv      0
 #define CODE_FOR_extv  CODE_FOR_nothing
 #define gen_extv(a,b,c,d) NULL_RTX
 #endif
 #ifndef HAVE_extzv
+#define HAVE_extzv     0
 #define CODE_FOR_extzv CODE_FOR_nothing
 #define gen_extzv(a,b,c,d) NULL_RTX
 #endif
@@ -103,16 +106,15 @@ static int mul_highpart_cost[NUM_MACHINE_MODES];
 void
 init_expmed ()
 {
-  /* This is "some random pseudo register" for purposes of calling recog
-     to see what insns exist.  */
-  rtx reg = gen_rtx_REG (word_mode, 10000);
-  rtx shift_insn, shiftadd_insn, shiftsub_insn;
+  rtx reg, shift_insn, shiftadd_insn, shiftsub_insn;
   int dummy;
   int m;
   enum machine_mode mode, wider_mode;
 
   start_sequence ();
 
+  /* This is "some random pseudo register" for purposes of calling recog
+     to see what insns exist.  */
   reg = gen_rtx_REG (word_mode, 10000);
 
   zero_cost = rtx_cost (const0_rtx, 0);
@@ -143,19 +145,18 @@ init_expmed ()
 
   for (m = 1; m < MAX_BITS_PER_WORD; m++)
     {
+      rtx c_int = GEN_INT ((HOST_WIDE_INT) 1 << m);
       shift_cost[m] = shiftadd_cost[m] = shiftsub_cost[m] = 32000;
 
       XEXP (SET_SRC (PATTERN (shift_insn)), 1) = GEN_INT (m);
       if (recog (PATTERN (shift_insn), shift_insn, &dummy) >= 0)
        shift_cost[m] = rtx_cost (SET_SRC (PATTERN (shift_insn)), SET);
 
-      XEXP (XEXP (SET_SRC (PATTERN (shiftadd_insn)), 0), 1)
-       = GEN_INT ((HOST_WIDE_INT) 1 << m);
+      XEXP (XEXP (SET_SRC (PATTERN (shiftadd_insn)), 0), 1) = c_int;
       if (recog (PATTERN (shiftadd_insn), shiftadd_insn, &dummy) >= 0)
        shiftadd_cost[m] = rtx_cost (SET_SRC (PATTERN (shiftadd_insn)), SET);
 
-      XEXP (XEXP (SET_SRC (PATTERN (shiftsub_insn)), 0), 1)
-       = GEN_INT ((HOST_WIDE_INT) 1 << m);
+      XEXP (XEXP (SET_SRC (PATTERN (shiftsub_insn)), 0), 1) = c_int;
       if (recog (PATTERN (shiftsub_insn), shiftsub_insn, &dummy) >= 0)
        shiftsub_cost[m] = rtx_cost (SET_SRC (PATTERN (shiftsub_insn)), SET);
     }
@@ -232,34 +233,31 @@ mode_for_extraction (pattern, opno)
   switch (pattern)
     {
     case EP_insv:
-#ifdef HAVE_insv
       if (HAVE_insv)
        {
          data = &insn_data[CODE_FOR_insv];
          break;
        }
-#endif
       return MAX_MACHINE_MODE;
 
     case EP_extv:
-#ifdef HAVE_extv
       if (HAVE_extv)
        {
          data = &insn_data[CODE_FOR_extv];
          break;
        }
-#endif
       return MAX_MACHINE_MODE;
 
     case EP_extzv:
-#ifdef HAVE_extzv
       if (HAVE_extzv)
        {
          data = &insn_data[CODE_FOR_extzv];
          break;
        }
-#endif
       return MAX_MACHINE_MODE;
+
+    default:
+      abort ();
     }
 
   if (opno == -1)
@@ -289,32 +287,22 @@ mode_for_extraction (pattern, opno)
    else, we use the mode of operand 3.  */
 
 rtx
-store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
+store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, total_size)
      rtx str_rtx;
      unsigned HOST_WIDE_INT bitsize;
      unsigned HOST_WIDE_INT bitnum;
      enum machine_mode fieldmode;
      rtx value;
-     unsigned int align;
      HOST_WIDE_INT total_size;
 {
   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 op0 = str_rtx;
+  int byte_offset;
 
-  unsigned HOST_WIDE_INT insv_bitsize;
-  enum machine_mode op_mode;
-
-  op_mode = mode_for_extraction (EP_insv, 3);
-  if (op_mode != MAX_MACHINE_MODE)
-    insv_bitsize = GET_MODE_BITSIZE (op_mode);
-
-  /* It is wrong to have align==0, since every object is aligned at
-     least at a bit boundary.  This usually means a bug elsewhere.  */
-  if (align == 0)
-    abort ();
+  enum machine_mode op_mode = mode_for_extraction (EP_insv, 3);
 
   /* Discount the part of the structure before the desired byte.
      We need to know how many bytes are safe to reference after it.  */
@@ -335,18 +323,15 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
       op0 = SUBREG_REG (op0);
     }
 
-  /* If OP0 is a register, BITPOS must count within a word.
-     But as we have it, it counts within whatever size OP0 now has.
-     On a bigendian machine, these are not the same, so convert.  */
-  if (BYTES_BIG_ENDIAN
-      && GET_CODE (op0) != MEM
-      && unit > GET_MODE_BITSIZE (GET_MODE (op0)))
-    bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
-
   value = protect_from_queue (value, 0);
 
   if (flag_force_mem)
-    value = force_not_mem (value);
+    {
+      int old_generating_concat_p = generating_concat_p;
+      generating_concat_p = 0;
+      value = force_not_mem (value);
+      generating_concat_p = old_generating_concat_p;
+    }
 
   /* If the target is a register, overwriting the entire object, or storing
      a full-word or multi-word field can be done with just a SUBREG.
@@ -354,15 +339,19 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
      If the target is memory, storing any naturally aligned field can be
      done with a simple store.  For targets that support fast unaligned
      memory, any naturally sized, unit aligned field can be done directly.  */
-     
-  if (bitsize == GET_MODE_BITSIZE (fieldmode)
+
+  byte_offset = (bitnum % BITS_PER_WORD) / BITS_PER_UNIT
+                + (offset * UNITS_PER_WORD);
+
+  if (bitpos == 0
+      && bitsize == GET_MODE_BITSIZE (fieldmode)
       && (GET_CODE (op0) != MEM
-         ? (GET_MODE_SIZE (fieldmode) >= UNITS_PER_WORD
+         ? ((GET_MODE_SIZE (fieldmode) >= UNITS_PER_WORD
             || GET_MODE_SIZE (GET_MODE (op0)) == GET_MODE_SIZE (fieldmode))
-         : (! SLOW_UNALIGNED_ACCESS (fieldmode, align)
+            && byte_offset % GET_MODE_SIZE (fieldmode) == 0)
+         : (! SLOW_UNALIGNED_ACCESS (fieldmode, MEM_ALIGN (op0))
             || (offset * BITS_PER_UNIT % bitsize == 0
-                && align % GET_MODE_BITSIZE (fieldmode) == 0)))
-      && (BYTES_BIG_ENDIAN ? bitpos + bitsize == unit : bitpos == 0))
+                && MEM_ALIGN (op0) % GET_MODE_BITSIZE (fieldmode) == 0))))
     {
       if (GET_MODE (op0) != fieldmode)
        {
@@ -379,9 +368,7 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
                abort ();
            }
          if (GET_CODE (op0) == REG)
-           op0 = gen_rtx_SUBREG (fieldmode, op0,
-                                 (bitnum % BITS_PER_WORD) / BITS_PER_UNIT
-                                 + (offset * UNITS_PER_WORD));
+           op0 = gen_rtx_SUBREG (fieldmode, op0, byte_offset);
          else
            op0 = adjust_address (op0, fieldmode, offset);
        }
@@ -406,6 +393,23 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
       }
   }
 
+  /* We may be accessing data outside the field, which means
+     we can alias adjacent data.  */
+  if (GET_CODE (op0) == MEM)
+    {
+      op0 = shallow_copy_rtx (op0);
+      set_mem_alias_set (op0, 0);
+      set_mem_expr (op0, 0);
+    }
+
+  /* If OP0 is a register, BITPOS must count within a word.
+     But as we have it, it counts within whatever size OP0 now has.
+     On a bigendian machine, these are not the same, so convert.  */
+  if (BYTES_BIG_ENDIAN
+      && GET_CODE (op0) != MEM
+      && unit > GET_MODE_BITSIZE (GET_MODE (op0)))
+    bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
+
   /* Storing an lsb-aligned field in a register
      can be done with a movestrict instruction.  */
 
@@ -477,10 +481,10 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
             if I is 1, use the next to lowest word; and so on.  */
          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);
+                                    ? 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),
@@ -489,7 +493,7 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
                                                  (GET_MODE (value) == VOIDmode
                                                   ? fieldmode
                                                   : GET_MODE (value))),
-                          align, total_size);
+                          total_size);
        }
       return value;
     }
@@ -524,31 +528,28 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
       offset = 0;
     }
   else
-    {
-      op0 = protect_from_queue (op0, 1);
-    }
+    op0 = protect_from_queue (op0, 1);
 
   /* If VALUE is a floating-point mode, access it as an integer of the
      corresponding size.  This can occur on a machine with 64 bit registers
      that uses SFmode for float.  This can also occur for unaligned float
      structure fields.  */
-  if (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT)
-    {
-      if (GET_CODE (value) != REG)
-       value = copy_to_reg (value);
-      value = gen_rtx_SUBREG (word_mode, value, 0);
-    }
+  if (GET_MODE_CLASS (GET_MODE (value)) != MODE_INT
+      && GET_MODE_CLASS (GET_MODE (value)) != MODE_PARTIAL_INT)
+    value = gen_lowpart ((GET_MODE (value) == VOIDmode
+                         ? word_mode : int_mode_for_mode (GET_MODE (value))),
+                        value);
 
   /* Now OFFSET is nonzero only if OP0 is memory
      and is therefore always measured in bytes.  */
 
-  if (op_mode != MAX_MACHINE_MODE
+  if (HAVE_insv
       && GET_MODE (value) != BLKmode
       && !(bitsize == 1 && GET_CODE (value) == CONST_INT)
       /* Ensure insv's size is wide enough for this field.  */
-      && (insv_bitsize >= bitsize)
+      && (GET_MODE_BITSIZE (op_mode) >= bitsize)
       && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
-           && (bitsize + bitpos > insv_bitsize)))
+           && (bitsize + bitpos > GET_MODE_BITSIZE (op_mode))))
     {
       int xbitpos = bitpos;
       rtx value1;
@@ -579,19 +580,19 @@ 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, maxmode,
+             = get_best_mode (bitsize, bitnum, MEM_ALIGN (op0), maxmode,
                               MEM_VOLATILE_P (op0));
          else
            bestmode = GET_MODE (op0);
 
          if (bestmode == VOIDmode
-             || (SLOW_UNALIGNED_ACCESS (bestmode, align)
-                 && GET_MODE_BITSIZE (bestmode) > align))
+             || (SLOW_UNALIGNED_ACCESS (bestmode, MEM_ALIGN (op0))
+                 && GET_MODE_BITSIZE (bestmode) > MEM_ALIGN (op0)))
            goto insv_loses;
 
-         /* Adjust address to point to the containing unit of that mode.  */
+         /* Adjust address to point to the containing unit of that mode.
+            Compute offset as multiple of this unit, counting in bytes.  */
          unit = GET_MODE_BITSIZE (bestmode);
-         /* Compute offset as multiple of this unit, counting in bytes.  */
          offset = (bitnum / unit) * GET_MODE_SIZE (bestmode);
          bitpos = bitnum % unit;
          op0 = adjust_address (op0, bestmode,  offset);
@@ -600,7 +601,7 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
             the unit.  */
          tempreg = copy_to_reg (op0);
          store_bit_field (tempreg, bitsize, bitpos, fieldmode, value,
-                          align, total_size);
+                          total_size);
          emit_move_insn (op0, tempreg);
          return value;
        }
@@ -644,16 +645,21 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
 
              if (GET_MODE_SIZE (GET_MODE (value)) < GET_MODE_SIZE (maxmode))
                {
-                 /* Avoid making subreg of a subreg, or of a mem.  */
-                 if (GET_CODE (value1) != REG)
-                   value1 = copy_to_reg (value1);
-                 value1 = gen_rtx_SUBREG (maxmode, value1, 0);
+                 rtx tmp;
+
+                 tmp = simplify_subreg (maxmode, value1, GET_MODE (value), 0);
+                 if (! tmp)
+                   tmp = simplify_gen_subreg (maxmode,
+                                              force_reg (GET_MODE (value),
+                                                         value1),
+                                              GET_MODE (value), 0);
+                 value1 = tmp;
                }
              else
                value1 = gen_lowpart (maxmode, value1);
            }
          else if (GET_CODE (value) == CONST_INT)
-           value1 = GEN_INT (trunc_int_for_mode (INTVAL (value), maxmode));
+           value1 = gen_int_mode (INTVAL (value), maxmode);
          else if (!CONSTANT_P (value))
            /* Parse phase is supposed to make VALUE's data type
               match that of the component reference, which is a type
@@ -672,15 +678,15 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
       if (pat)
        emit_insn (pat);
       else
-        {
+       {
          delete_insns_since (last);
-         store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align);
+         store_fixed_bit_field (op0, offset, bitsize, bitpos, value);
        }
     }
   else
     insv_loses:
     /* Insv is not available; store using shifts and boolean ops.  */
-    store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align);
+    store_fixed_bit_field (op0, offset, bitsize, bitpos, value);
   return value;
 }
 \f
@@ -692,26 +698,21 @@ 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.
 
    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;
+store_fixed_bit_field (op0, offset, bitsize, bitpos, value)
+     rtx op0;
      unsigned HOST_WIDE_INT offset, bitsize, bitpos;
-     register rtx value;
-     unsigned int struct_align;
+     rtx value;
 {
-  register enum machine_mode mode;
+  enum machine_mode mode;
   unsigned int total_bits = BITS_PER_WORD;
   rtx subtarget, temp;
   int all_zero = 0;
   int all_one = 0;
 
-  if (! SLOW_UNALIGNED_ACCESS (word_mode, struct_align))
-    struct_align = BIGGEST_ALIGNMENT;
-    
   /* There is a case not handled here:
      a structure with a known alignment of just a halfword
      and a field split across two aligned halfwords within the structure.
@@ -726,8 +727,7 @@ store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)
       /* Special treatment for a bit field split across two registers.  */
       if (bitsize + bitpos > BITS_PER_WORD)
        {
-         store_split_bit_field (op0, bitsize, bitpos,
-                                value, BITS_PER_WORD);
+         store_split_bit_field (op0, bitsize, bitpos, value);
          return;
        }
     }
@@ -735,24 +735,22 @@ store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)
     {
       /* Get the proper mode to use for this field.  We want a mode that
         includes the entire field.  If such a mode would be larger than
-        a word, we won't be doing the extraction the normal way.  
+        a word, we won't be doing the extraction the normal way.
         We don't want a mode bigger than the destination.  */
 
       mode = GET_MODE (op0);
       if (GET_MODE_BITSIZE (mode) == 0
-          || GET_MODE_BITSIZE (mode) > GET_MODE_BITSIZE (word_mode))
-        mode = word_mode;
+         || GET_MODE_BITSIZE (mode) > GET_MODE_BITSIZE (word_mode))
+       mode = word_mode;
       mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT,
-                           struct_align, mode,
-                           GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0));
+                           MEM_ALIGN (op0), mode, MEM_VOLATILE_P (op0));
 
       if (mode == VOIDmode)
        {
          /* The only way this should occur is if the field spans word
             boundaries.  */
-         store_split_bit_field (op0,
-                                bitsize, bitpos + offset * BITS_PER_UNIT,
-                                value, struct_align);
+         store_split_bit_field (op0, bitsize, bitpos + offset * BITS_PER_UNIT,
+                                value);
          return;
        }
 
@@ -799,7 +797,7 @@ store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)
 
   if (GET_CODE (value) == CONST_INT)
     {
-      register HOST_WIDE_INT v = INTVAL (value);
+      HOST_WIDE_INT v = INTVAL (value);
 
       if (bitsize < HOST_BITS_PER_WIDE_INT)
        v &= ((HOST_WIDE_INT) 1 << bitsize) - 1;
@@ -866,17 +864,14 @@ 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.
-   This is also the size of the memory objects to be used.
 
    This does not yet handle fields wider than BITS_PER_WORD.  */
 
 static void
-store_split_bit_field (op0, bitsize, bitpos, value, align)
+store_split_bit_field (op0, bitsize, bitpos, value)
      rtx op0;
      unsigned HOST_WIDE_INT bitsize, bitpos;
      rtx value;
-     unsigned int align;
 {
   unsigned int unit;
   unsigned int bitsdone = 0;
@@ -886,7 +881,7 @@ store_split_bit_field (op0, bitsize, bitpos, value, align)
   if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
     unit = BITS_PER_WORD;
   else
-    unit = MIN (align, BITS_PER_WORD);
+    unit = MIN (MEM_ALIGN (op0), 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
@@ -942,18 +937,10 @@ store_split_bit_field (op0, bitsize, bitpos, value, align)
          else
            /* The args are chosen so that the last part includes the
               lsb.  Give extract_bit_field the value it needs (with
-              endianness compensation) to fetch the piece we want.
-
-              ??? We have no idea what the alignment of VALUE is, so
-              we have to use a guess.  */
-           part
-             = extract_fixed_bit_field
-               (word_mode, value, 0, thissize,
-                total_bits - bitsize + bitsdone, NULL_RTX, 1,
-                GET_MODE (value) == VOIDmode
-                ? UNITS_PER_WORD
-                : (GET_MODE (value) == BLKmode
-                   ? 1 : GET_MODE_ALIGNMENT (GET_MODE (value))));
+              endianness compensation) to fetch the piece we want.  */
+           part = extract_fixed_bit_field (word_mode, value, 0, thissize,
+                                           total_bits - bitsize + bitsdone,
+                                           NULL_RTX, 1);
        }
       else
        {
@@ -963,13 +950,8 @@ store_split_bit_field (op0, bitsize, bitpos, value, align)
                             >> bitsdone)
                            & (((HOST_WIDE_INT) 1 << thissize) - 1));
          else
-           part
-             = extract_fixed_bit_field
-               (word_mode, value, 0, thissize, bitsdone, NULL_RTX, 1,
-                GET_MODE (value) == VOIDmode
-                ? UNITS_PER_WORD
-                : (GET_MODE (value) == BLKmode
-                   ? 1 : GET_MODE_ALIGNMENT (GET_MODE (value))));
+           part = extract_fixed_bit_field (word_mode, value, 0, thissize,
+                                           bitsdone, NULL_RTX, 1);
        }
 
       /* If OP0 is a register, then handle OFFSET here.
@@ -995,8 +977,8 @@ store_split_bit_field (op0, bitsize, bitpos, value, align)
 
       /* OFFSET is in UNITs, and UNIT is in bits.
          store_fixed_bit_field wants offset in bytes.  */
-      store_fixed_bit_field (word, offset * unit / BITS_PER_UNIT,
-                            thissize, thispos, part, align);
+      store_fixed_bit_field (word, offset * unit / BITS_PER_UNIT, thissize,
+                            thispos, part);
       bitsdone += thissize;
     }
 }
@@ -1013,7 +995,6 @@ 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.
    TOTAL_SIZE is the size in bytes of the containing structure,
    or -1 if varying.
 
@@ -1024,36 +1005,27 @@ store_split_bit_field (op0, bitsize, bitpos, value, align)
 
 rtx
 extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
-                  target, mode, tmode, align, total_size)
+                  target, mode, tmode, total_size)
      rtx str_rtx;
      unsigned HOST_WIDE_INT bitsize;
      unsigned HOST_WIDE_INT bitnum;
      int unsignedp;
      rtx target;
      enum machine_mode mode, tmode;
-     unsigned int align;
      HOST_WIDE_INT total_size;
 {
   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 op0 = str_rtx;
   rtx spec_target = target;
   rtx spec_target_subreg = 0;
   enum machine_mode int_mode;
-  unsigned HOST_WIDE_INT extv_bitsize;
-  enum machine_mode extv_mode;
-  unsigned HOST_WIDE_INT extzv_bitsize;
-  enum machine_mode extzv_mode;
-
-  extv_mode = mode_for_extraction (EP_extv, 0);
-  if (extv_mode != MAX_MACHINE_MODE)
-    extv_bitsize = GET_MODE_BITSIZE (extv_mode);
-
-  extzv_mode = mode_for_extraction (EP_extzv, 0);
-  if (extzv_mode != MAX_MACHINE_MODE)
-    extzv_bitsize = GET_MODE_BITSIZE (extzv_mode);
+  enum machine_mode extv_mode = mode_for_extraction (EP_extv, 0);
+  enum machine_mode extzv_mode = mode_for_extraction (EP_extzv, 0);
+  enum machine_mode mode1;
+  int byte_offset;
 
   /* Discount the part of the structure before the desired byte.
      We need to know how many bytes are safe to reference after it.  */
@@ -1063,25 +1035,15 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
 
   if (tmode == VOIDmode)
     tmode = mode;
+
   while (GET_CODE (op0) == SUBREG)
     {
-      int outer_size = GET_MODE_BITSIZE (GET_MODE (op0));
-      int inner_size = GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)));
-
-      offset += SUBREG_BYTE (op0) / UNITS_PER_WORD;
-
-      inner_size = MIN (inner_size, BITS_PER_WORD);
-
-      if (BYTES_BIG_ENDIAN && (outer_size < inner_size))
+      bitpos += SUBREG_BYTE (op0) * BITS_PER_UNIT;
+      if (bitpos > unit)
        {
-         bitpos += inner_size - outer_size;
-         if (bitpos > unit)
-           {
-             offset += (bitpos / unit);
-             bitpos %= unit;
-           }
+         offset += (bitpos / unit);
+         bitpos %= unit;
        }
-
       op0 = SUBREG_REG (op0);
     }
 
@@ -1109,10 +1071,23 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
       }
   }
 
-  /* ??? 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.  */
-  
+  /* We may be accessing data outside the field, which means
+     we can alias adjacent data.  */
+  if (GET_CODE (op0) == MEM)
+    {
+      op0 = shallow_copy_rtx (op0);
+      set_mem_alias_set (op0, 0);
+      set_mem_expr (op0, 0);
+    }
+
+  /* Extraction of a full-word or multi-word value from a structure
+     in a register or aligned memory can be done with just a SUBREG.
+     A subword value in the least significant part of a register
+     can also be extracted with a SUBREG.  For this, we need the
+     byte offset of the value in op0.  */
+
+  byte_offset = bitpos / BITS_PER_UNIT + offset * UNITS_PER_WORD;
+
   /* 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.  */
@@ -1121,19 +1096,23 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
       && unit > GET_MODE_BITSIZE (GET_MODE (op0)))
     bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
 
-  /* Extracting a full-word or multi-word value
-     from a structure in a register or aligned memory.
-     This can be done with just SUBREG.
-     So too extracting a subword value in
-     the least significant part of the register.  */
+  /* ??? 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.  */
+
+  mode1  = (VECTOR_MODE_P (tmode)
+           ? mode
+           : mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0));
 
   if (((GET_CODE (op0) != MEM
        && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
-                                 GET_MODE_BITSIZE (GET_MODE (op0))))
+                                 GET_MODE_BITSIZE (GET_MODE (op0)))
+       && GET_MODE_SIZE (mode1) != 0
+       && byte_offset % GET_MODE_SIZE (mode1) == 0)
        || (GET_CODE (op0) == MEM
-          && (! SLOW_UNALIGNED_ACCESS (mode, align)
+          && (! SLOW_UNALIGNED_ACCESS (mode, MEM_ALIGN (op0))
               || (offset * BITS_PER_UNIT % bitsize == 0
-                  && align % bitsize == 0))))
+                  && MEM_ALIGN (op0) % 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
@@ -1145,10 +1124,6 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
                  ? bitpos + bitsize == BITS_PER_WORD
                  : bitpos == 0))))
     {
-      enum machine_mode mode1
-       = (VECTOR_MODE_P (tmode) ? mode
-          : mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0));
-
       if (mode1 != GET_MODE (op0))
        {
          if (GET_CODE (op0) == SUBREG)
@@ -1161,12 +1136,10 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
                /* 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 ();
+               goto no_subreg_mode_swap;
            }
          if (GET_CODE (op0) == REG)
-           op0 = gen_rtx_SUBREG (mode1, op0,
-                                 (bitnum % BITS_PER_WORD) / BITS_PER_UNIT
-                                 + (offset * UNITS_PER_WORD));
+           op0 = gen_rtx_SUBREG (mode1, op0, byte_offset);
          else
            op0 = adjust_address (op0, mode1, offset);
        }
@@ -1174,9 +1147,10 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
        return convert_to_mode (tmode, op0, unsignedp);
       return op0;
     }
+ no_subreg_mode_swap:
 
   /* Handle fields bigger than a word.  */
-  
+
   if (bitsize > BITS_PER_WORD)
     {
       /* Here we transfer the words of the field
@@ -1212,7 +1186,7 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
            = 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);
+                                word_mode, total_size);
 
          if (target_part == 0)
            abort ();
@@ -1231,11 +1205,11 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
 
              total_words = GET_MODE_SIZE (GET_MODE (target)) / UNITS_PER_WORD;
              for (i = nwords; i < total_words; i++)
-               {
-                 int wordnum = WORDS_BIG_ENDIAN ? total_words - i - 1 : i;
-                 rtx target_part = operand_subword (target, wordnum, 1, VOIDmode);
-                 emit_move_insn (target_part, const0_rtx);
-               }
+               emit_move_insn
+                 (operand_subword (target,
+                                   WORDS_BIG_ENDIAN ? total_words - i - 1 : i,
+                                   1, VOIDmode),
+                  const0_rtx);
            }
          return target;
        }
@@ -1248,20 +1222,20 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
                           build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0),
                           NULL_RTX, 0);
     }
-  
+
   /* 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.  */
+    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.  */
@@ -1279,18 +1253,16 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
       offset = 0;
     }
   else
-    {
-      op0 = protect_from_queue (str_rtx, 1);
-    }
+    op0 = protect_from_queue (str_rtx, 1);
 
   /* Now OFFSET is nonzero only for memory operands.  */
 
   if (unsignedp)
     {
-      if (extzv_mode != MAX_MACHINE_MODE
-         && (extzv_bitsize >= bitsize)
+      if (HAVE_extzv
+         && (GET_MODE_BITSIZE (extzv_mode) >= bitsize)
          && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
-               && (bitsize + bitpos > extzv_bitsize)))
+               && (bitsize + bitpos > GET_MODE_BITSIZE (extzv_mode))))
        {
          unsigned HOST_WIDE_INT xbitpos = bitpos, xoffset = offset;
          rtx bitsize_rtx, bitpos_rtx;
@@ -1323,14 +1295,15 @@ 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, maxmode,
+                   bestmode = get_best_mode (bitsize, bitnum,
+                                             MEM_ALIGN (xop0), maxmode,
                                              MEM_VOLATILE_P (xop0));
                  else
                    bestmode = GET_MODE (xop0);
 
                  if (bestmode == VOIDmode
-                     || (SLOW_UNALIGNED_ACCESS (bestmode, align)
-                         && GET_MODE_BITSIZE (bestmode) > align))
+                     || (SLOW_UNALIGNED_ACCESS (bestmode, MEM_ALIGN (xop0))
+                         && GET_MODE_BITSIZE (bestmode) > MEM_ALIGN (xop0)))
                    goto extzv_loses;
 
                  /* Compute offset as multiple of this unit,
@@ -1410,20 +1383,20 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
            {
              delete_insns_since (last);
              target = extract_fixed_bit_field (int_mode, op0, offset, bitsize,
-                                               bitpos, target, 1, align);
+                                               bitpos, target, 1);
            }
        }
       else
-        extzv_loses:
-      target = extract_fixed_bit_field (int_mode, op0, offset, bitsize, 
-                                       bitpos, target, 1, align);
+      extzv_loses:
+       target = extract_fixed_bit_field (int_mode, op0, offset, bitsize,
+                                         bitpos, target, 1);
     }
   else
     {
-      if (extv_mode != MAX_MACHINE_MODE
-         && (extv_bitsize >= bitsize)
+      if (HAVE_extv
+         && (GET_MODE_BITSIZE (extv_mode) >= bitsize)
          && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
-               && (bitsize + bitpos > extv_bitsize)))
+               && (bitsize + bitpos > GET_MODE_BITSIZE (extv_mode))))
        {
          int xbitpos = bitpos, xoffset = offset;
          rtx bitsize_rtx, bitpos_rtx;
@@ -1452,14 +1425,15 @@ 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, maxmode,
+                   bestmode = get_best_mode (bitsize, bitnum,
+                                             MEM_ALIGN (xop0), maxmode,
                                              MEM_VOLATILE_P (xop0));
                  else
                    bestmode = GET_MODE (xop0);
 
                  if (bestmode == VOIDmode
-                     || (SLOW_UNALIGNED_ACCESS (bestmode, align)
-                         && GET_MODE_BITSIZE (bestmode) > align))
+                     || (SLOW_UNALIGNED_ACCESS (bestmode, MEM_ALIGN (xop0))
+                         && GET_MODE_BITSIZE (bestmode) > MEM_ALIGN (xop0)))
                    goto extv_loses;
 
                  /* Compute offset as multiple of this unit,
@@ -1538,13 +1512,13 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
            {
              delete_insns_since (last);
              target = extract_fixed_bit_field (int_mode, op0, offset, bitsize,
-                                               bitpos, target, 0, align);
+                                               bitpos, target, 0);
            }
-       } 
+       }
       else
-       extv_loses:
-      target = extract_fixed_bit_field (int_mode, op0, offset, bitsize, 
-                                       bitpos, target, 0, align);
+      extv_loses:
+       target = extract_fixed_bit_field (int_mode, op0, offset, bitsize,
+                                         bitpos, target, 0);
     }
   if (target == spec_target)
     return target;
@@ -1555,14 +1529,13 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
       /* If the target mode is floating-point, first convert to the
         integer mode of that size and then access it as a floating-point
         value via a SUBREG.  */
-      if (GET_MODE_CLASS (tmode) == MODE_FLOAT)
+      if (GET_MODE_CLASS (tmode) != MODE_INT
+         && GET_MODE_CLASS (tmode) != MODE_PARTIAL_INT)
        {
          target = convert_to_mode (mode_for_size (GET_MODE_BITSIZE (tmode),
                                                   MODE_INT, 0),
                                    target, unsignedp);
-         if (GET_CODE (target) != REG)
-           target = copy_to_reg (target);
-         return gen_rtx_SUBREG (tmode, target, 0);
+         return gen_lowpart (tmode, target);
        }
       else
        return convert_to_mode (tmode, target, unsignedp);
@@ -1584,18 +1557,15 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
    UNSIGNEDP is nonzero for an unsigned bit field (don't sign-extend value).
    If TARGET is nonzero, attempts to store the value there
    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.  */
+   If TARGET is not used, create a pseudo-reg of mode TMODE for the value.  */
 
 static rtx
 extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
-                        target, unsignedp, align)
+                        target, unsignedp)
      enum machine_mode tmode;
-     register rtx op0, target;
+     rtx op0, target;
      unsigned HOST_WIDE_INT offset, bitsize, bitpos;
      int unsignedp;
-     unsigned int align;
 {
   unsigned int total_bits = BITS_PER_WORD;
   enum machine_mode mode;
@@ -1604,8 +1574,7 @@ extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
     {
       /* Special treatment for a bit field split across two registers.  */
       if (bitsize + bitpos > BITS_PER_WORD)
-       return extract_split_bit_field (op0, bitsize, bitpos,
-                                       unsignedp, align);
+       return extract_split_bit_field (op0, bitsize, bitpos, unsignedp);
     }
   else
     {
@@ -1613,16 +1582,15 @@ 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,
-                           word_mode,
-                           GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0));
+      mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT,
+                           MEM_ALIGN (op0), word_mode, MEM_VOLATILE_P (op0));
 
       if (mode == VOIDmode)
        /* The only way this should occur is if the field spans word
           boundaries.  */
        return extract_split_bit_field (op0, bitsize,
                                        bitpos + offset * BITS_PER_UNIT,
-                                       unsignedp, align);
+                                       unsignedp);
 
       total_bits = GET_MODE_BITSIZE (mode);
 
@@ -1648,12 +1616,9 @@ extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
   mode = GET_MODE (op0);
 
   if (BYTES_BIG_ENDIAN)
-    {
-      /* BITPOS is the distance between our msb and that of OP0.
-        Convert it to the distance from the lsb.  */
-
-      bitpos = total_bits - bitsize - bitpos;
-    }
+    /* BITPOS is the distance between our msb and that of OP0.
+       Convert it to the distance from the lsb.  */
+    bitpos = total_bits - bitsize - bitpos;
 
   /* Now BITPOS is always the distance between the field's lsb and that of OP0.
      We have reduced the big-endian case to the little-endian case.  */
@@ -1680,16 +1645,7 @@ extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
       /* Unless the msb of the field used to be the msb when we shifted,
         mask out the upper bits.  */
 
-      if (GET_MODE_BITSIZE (mode) != bitpos + bitsize
-#if 0
-#ifdef SLOW_ZERO_EXTEND
-         /* Always generate an `and' if
-            we just zero-extended op0 and SLOW_ZERO_EXTEND, since it
-            will combine fruitfully with the zero-extend.  */
-         || tmode != mode
-#endif
-#endif
-         )
+      if (GET_MODE_BITSIZE (mode) != bitpos + bitsize)
        return expand_binop (GET_MODE (op0), and_optab, op0,
                             mask_rtx (GET_MODE (op0), 0, bitsize, 0),
                             target, 1, OPTAB_LIB_WIDEN);
@@ -1714,7 +1670,8 @@ extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
 
   if (GET_MODE_BITSIZE (mode) != (bitsize + bitpos))
     {
-      tree amount = build_int_2 (GET_MODE_BITSIZE (mode) - (bitsize + bitpos), 0);
+      tree amount
+       = build_int_2 (GET_MODE_BITSIZE (mode) - (bitsize + bitpos), 0);
       /* Maybe propagate the target for the shift.  */
       /* But not if we will return the result--could confuse integrate.c.  */
       rtx subtarget = (target != 0 && GET_CODE (target) == REG
@@ -1724,7 +1681,7 @@ extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
     }
 
   return expand_shift (RSHIFT_EXPR, mode, op0,
-                      build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0), 
+                      build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0),
                       target, 0);
 }
 \f
@@ -1749,7 +1706,7 @@ mask_rtx (mode, bitpos, bitsize, complement)
   if (bitpos + bitsize < HOST_BITS_PER_WIDE_INT)
     masklow &= ((unsigned HOST_WIDE_INT) -1
                >> (HOST_BITS_PER_WIDE_INT - bitpos - bitsize));
-  
+
   if (bitpos <= HOST_BITS_PER_WIDE_INT)
     maskhigh = -1;
   else
@@ -1804,17 +1761,13 @@ lshift_value (mode, value, bitpos, bitsize)
 
    OP0 is the REG, SUBREG or MEM rtx for the first of the two words.
    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.  This is also the size of the
-   memory objects to be used.  */
+   UNSIGNEDP is 1 if should zero-extend the contents; else sign-extend.  */
 
 static rtx
-extract_split_bit_field (op0, bitsize, bitpos, unsignedp, align)
+extract_split_bit_field (op0, bitsize, bitpos, unsignedp)
      rtx op0;
      unsigned HOST_WIDE_INT bitsize, bitpos;
      int unsignedp;
-     unsigned int align;
 {
   unsigned int unit;
   unsigned int bitsdone = 0;
@@ -1826,7 +1779,7 @@ 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_WORD);
+    unit = MIN (MEM_ALIGN (op0), BITS_PER_WORD);
 
   while (bitsdone < bitsize)
     {
@@ -1871,7 +1824,7 @@ extract_split_bit_field (op0, bitsize, bitpos, unsignedp, align)
         extract_fixed_bit_field wants offset in bytes.  */
       part = extract_fixed_bit_field (word_mode, word,
                                      offset * unit / BITS_PER_UNIT,
-                                     thissize, thispos, 0, 1, align);
+                                     thissize, thispos, 0, 1);
       bitsdone += thissize;
 
       /* Shift this part into place for the result.  */
@@ -1946,15 +1899,15 @@ expand_dec (target, dec)
 rtx
 expand_shift (code, mode, shifted, amount, target, unsignedp)
      enum tree_code code;
-     register enum machine_mode mode;
+     enum machine_mode mode;
      rtx shifted;
      tree amount;
-     register rtx target;
+     rtx target;
      int unsignedp;
 {
-  register rtx op1, temp = 0;
-  register int left = (code == LSHIFT_EXPR || code == LROTATE_EXPR);
-  register int rotate = (code == LROTATE_EXPR || code == RROTATE_EXPR);
+  rtx op1, temp = 0;
+  int left = (code == LSHIFT_EXPR || code == LROTATE_EXPR);
+  int rotate = (code == LROTATE_EXPR || code == RROTATE_EXPR);
   int try;
 
   /* Previously detected shift-counts computed by NEGATE_EXPR
@@ -1967,12 +1920,12 @@ expand_shift (code, mode, shifted, amount, target, unsignedp)
   if (SHIFT_COUNT_TRUNCATED)
     {
       if (GET_CODE (op1) == CONST_INT
-          && ((unsigned HOST_WIDE_INT) INTVAL (op1) >=
+         && ((unsigned HOST_WIDE_INT) INTVAL (op1) >=
              (unsigned HOST_WIDE_INT) GET_MODE_BITSIZE (mode)))
-        op1 = GEN_INT ((unsigned HOST_WIDE_INT) INTVAL (op1)
+       op1 = GEN_INT ((unsigned HOST_WIDE_INT) INTVAL (op1)
                       % GET_MODE_BITSIZE (mode));
       else if (GET_CODE (op1) == SUBREG
-              && SUBREG_BYTE (op1) == 0)
+              && subreg_lowpart_p (op1))
        op1 = SUBREG_REG (op1);
     }
 #endif
@@ -2043,7 +1996,7 @@ expand_shift (code, mode, shifted, amount, target, unsignedp)
              && (unsigned int) INTVAL (op1) < GET_MODE_BITSIZE (mode))
            temp = expand_binop (mode,
                                 left ? rotr_optab : rotl_optab,
-                                shifted, 
+                                shifted,
                                 GEN_INT (GET_MODE_BITSIZE (mode)
                                          - INTVAL (op1)),
                                 target, unsignedp, methods);
@@ -2074,7 +2027,7 @@ expand_shift (code, mode, shifted, amount, target, unsignedp)
        }
 
       /* We used to try extzv here for logical right shifts, but that was
-        only useful for one machine, the VAX, and caused poor code 
+        only useful for one machine, the VAX, and caused poor code
         generation there for lshrdi3, so the code was deleted and a
         define_expand for lshrsi3 was added to vax.md.  */
     }
@@ -2215,7 +2168,7 @@ synth_mult (alg_out, t, cost_limit)
       for (w = 1; (w & t) != 0; w <<= 1)
        ;
       /* If T was -1, then W will be zero after the loop.  This is another
-        case where T ends with ...111.  Handling this with (T + 1) and 
+        case where T ends with ...111.  Handling this with (T + 1) and
         subtract 1 produces slightly better code and results in algorithm
         selection much faster than treating it like the ...0111 case
         below.  */
@@ -2386,7 +2339,7 @@ synth_mult (alg_out, t, cost_limit)
 rtx
 expand_mult (mode, op0, op1, target, unsignedp)
      enum machine_mode mode;
-     register rtx op0, op1, target;
+     rtx op0, op1, target;
      int unsignedp;
 {
   rtx const_op1 = op1;
@@ -2426,6 +2379,10 @@ expand_mult (mode, op0, op1, target, unsignedp)
       int mult_cost;
       enum {basic_variant, negate_variant, add_variant} variant = basic_variant;
 
+      /* op0 must be register to make mult_cost match the precomputed
+         shiftadd_cost array.  */
+      op0 = force_reg (mode, op0);
+
       /* Try to do the computation three ways: multiply by the negative of OP1
         and then negate, do the multiplication directly, or do multiplication
         by OP1 - 1.  */
@@ -2491,7 +2448,7 @@ expand_mult (mode, op0, op1, target, unsignedp)
                   && ! preserve)
                  ? target : 0;
              rtx accum_target = preserve ? 0 : accum;
-             
+
              switch (alg.op[opno])
                {
                case alg_shift:
@@ -2573,7 +2530,7 @@ expand_mult (mode, op0, op1, target, unsignedp)
                }
 
              insn = get_last_insn ();
-             set_unique_reg_note (insn, 
+             set_unique_reg_note (insn,
                                   REG_EQUAL,
                                   gen_rtx_MULT (nmode, tem,
                                                 GEN_INT (val_so_far)));
@@ -2599,10 +2556,10 @@ expand_mult (mode, op0, op1, target, unsignedp)
 
   /* This used to use umul_optab if unsigned, but for non-widening multiply
      there is no difference between signed and unsigned.  */
-  op0 = expand_binop (mode, 
+  op0 = expand_binop (mode,
                      ! unsignedp
-                       && flag_trapv && (GET_MODE_CLASS(mode) == MODE_INT)
-                       ? smulv_optab : smul_optab,
+                     && flag_trapv && (GET_MODE_CLASS(mode) == MODE_INT)
+                     ? smulv_optab : smul_optab,
                      op0, op1, target, unsignedp, OPTAB_LIB_WIDEN);
   if (op0 == 0)
     abort ();
@@ -2695,7 +2652,7 @@ choose_multiplier (d, n, precision, multiplier_ptr, post_shift_ptr, lgup_ptr)
     abort ();
   /* assert that mlow < mhigh.  */
   if (! (mlow_hi < mhigh_hi || (mlow_hi == mhigh_hi && mlow_lo < mhigh_lo)))
-    abort();
+    abort ();
 
   /* If precision == N, then mlow, mhigh exceed 2^N
      (but they do not exceed 2^(N+1)).  */
@@ -2772,7 +2729,7 @@ invert_mod2n (x, n)
 rtx
 expand_mult_highpart_adjust (mode, adj_operand, op0, op1, target, unsignedp)
      enum machine_mode mode;
-     register rtx adj_operand, op0, op1, target;
+     rtx adj_operand, op0, op1, target;
      int unsignedp;
 {
   rtx tem;
@@ -2781,7 +2738,7 @@ expand_mult_highpart_adjust (mode, adj_operand, op0, op1, target, unsignedp)
   tem = expand_shift (RSHIFT_EXPR, mode, op0,
                      build_int_2 (GET_MODE_BITSIZE (mode) - 1, 0),
                      NULL_RTX, 0);
-  tem = expand_and (tem, op1, NULL_RTX);
+  tem = expand_and (mode, tem, op1, NULL_RTX);
   adj_operand
     = force_operand (gen_rtx_fmt_ee (adj_code, mode, adj_operand, tem),
                     adj_operand);
@@ -2789,7 +2746,7 @@ expand_mult_highpart_adjust (mode, adj_operand, op0, op1, target, unsignedp)
   tem = expand_shift (RSHIFT_EXPR, mode, op1,
                      build_int_2 (GET_MODE_BITSIZE (mode) - 1, 0),
                      NULL_RTX, 0);
-  tem = expand_and (tem, op0, NULL_RTX);
+  tem = expand_and (mode, tem, op0, NULL_RTX);
   target = force_operand (gen_rtx_fmt_ee (adj_code, mode, adj_operand, tem),
                          target);
 
@@ -2809,7 +2766,7 @@ expand_mult_highpart_adjust (mode, adj_operand, op0, op1, target, unsignedp)
 rtx
 expand_mult_highpart (mode, op0, cnst1, target, unsignedp, max_cost)
      enum machine_mode mode;
-     register rtx op0, target;
+     rtx op0, target;
      unsigned HOST_WIDE_INT cnst1;
      int unsignedp;
      int max_cost;
@@ -2825,17 +2782,14 @@ expand_mult_highpart (mode, op0, cnst1, target, unsignedp, max_cost)
   if (size > HOST_BITS_PER_WIDE_INT)
     abort ();
 
-  op1 = GEN_INT (trunc_int_for_mode (cnst1, mode));
+  op1 = gen_int_mode (cnst1, mode);
 
-  if (GET_MODE_BITSIZE (wider_mode) <= HOST_BITS_PER_INT)
-    wide_op1 = op1;
-  else
-    wide_op1
-      = immed_double_const (cnst1,
-                           (unsignedp
-                            ? (HOST_WIDE_INT) 0
-                            : -(cnst1 >> (HOST_BITS_PER_WIDE_INT - 1))),
-                           wider_mode);
+  wide_op1
+    = immed_double_const (cnst1,
+                         (unsignedp
+                          ? (HOST_WIDE_INT) 0
+                          : -(cnst1 >> (HOST_BITS_PER_WIDE_INT - 1))),
+                         wider_mode);
 
   /* expand_mult handles constant multiplication of word_mode
      or narrower.  It does a poor job for large modes.  */
@@ -2890,7 +2844,7 @@ expand_mult_highpart (mode, op0, cnst1, target, unsignedp, max_cost)
     {
       op1 = force_reg (mode, op1);
       goto try;
-    } 
+    }
 
   /* Try widening the mode and perform a non-widening multiplication.  */
   moptab = smul_optab;
@@ -2970,7 +2924,7 @@ expand_mult_highpart (mode, op0, cnst1, target, unsignedp, max_cost)
    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
+   so that if a 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,
@@ -2991,24 +2945,30 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
      int rem_flag;
      enum tree_code code;
      enum machine_mode mode;
-     register rtx op0, op1, target;
+     rtx op0, op1, target;
      int unsignedp;
 {
   enum machine_mode compute_mode;
-  register rtx tquotient;
+  rtx tquotient;
   rtx quotient = 0, remainder = 0;
   rtx last;
   int size;
   rtx insn, set;
   optab optab1, optab2;
-  int op1_is_constant, op1_is_pow2;
+  int op1_is_constant, op1_is_pow2 = 0;
   int max_cost, extra_cost;
   static HOST_WIDE_INT last_div_const = 0;
+  static HOST_WIDE_INT ext_op1;
 
   op1_is_constant = GET_CODE (op1) == CONST_INT;
-  op1_is_pow2 = (op1_is_constant
-                && ((EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1))
-                     || (! unsignedp && EXACT_POWER_OF_2_OR_ZERO_P (-INTVAL (op1))))));
+  if (op1_is_constant)
+    {
+      ext_op1 = INTVAL (op1);
+      if (unsignedp)
+       ext_op1 &= GET_MODE_MASK (mode);
+      op1_is_pow2 = ((EXACT_POWER_OF_2_OR_ZERO_P (ext_op1)
+                    || (! unsignedp && EXACT_POWER_OF_2_OR_ZERO_P (-ext_op1))));
+    }
 
   /*
      This is the structure of expand_divmod:
@@ -3050,9 +3010,9 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
   if (! unsignedp && op1 == constm1_rtx)
     {
       if (rem_flag)
-        return const0_rtx;
+       return const0_rtx;
       return expand_unop (mode, flag_trapv && GET_MODE_CLASS(mode) == MODE_INT
-                        ? negv_optab : neg_optab, op0, target, 0);
+                         ? negv_optab : neg_optab, op0, target, 0);
     }
 
   if (target
@@ -3088,9 +3048,12 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
      not straightforward to generalize this.  Maybe we should make an array
      of possible modes in init_expmed?  Save this for GCC 2.7.  */
 
-  optab1 = (op1_is_pow2 ? (unsignedp ? lshr_optab : ashr_optab)
+  optab1 = ((op1_is_pow2 && op1 != const0_rtx)
+           ? (unsignedp ? lshr_optab : ashr_optab)
            : (unsignedp ? udiv_optab : sdiv_optab));
-  optab2 = (op1_is_pow2 ? optab1 : (unsignedp ? udivmod_optab : sdivmod_optab));
+  optab2 = ((op1_is_pow2 && op1 != const0_rtx)
+           ? optab1
+           : (unsignedp ? udivmod_optab : sdivmod_optab));
 
   for (compute_mode = mode; compute_mode != VOIDmode;
        compute_mode = GET_MODE_WIDER_MODE (compute_mode))
@@ -3185,7 +3148,8 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                unsigned HOST_WIDE_INT mh, ml;
                int pre_shift, post_shift;
                int dummy;
-               unsigned HOST_WIDE_INT d = INTVAL (op1);
+               unsigned HOST_WIDE_INT d = (INTVAL (op1)
+                                           & GET_MODE_MASK (compute_mode));
 
                if (EXACT_POWER_OF_2_OR_ZERO_P (d))
                  {
@@ -3297,7 +3261,7 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                if (insn != last
                    && (set = single_set (insn)) != 0
                    && SET_DEST (set) == quotient)
-                 set_unique_reg_note (insn, 
+                 set_unique_reg_note (insn,
                                       REG_EQUAL,
                                       gen_rtx_UDIV (compute_mode, op0, op1));
              }
@@ -3312,7 +3276,7 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                if (rem_flag && d < 0)
                  {
                    d = abs_d;
-                   op1 = GEN_INT (trunc_int_for_mode (abs_d, compute_mode));
+                   op1 = gen_int_mode (abs_d, compute_mode);
                  }
 
                if (d == 1)
@@ -3329,7 +3293,16 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                      goto fail1;
                  }
                else if (EXACT_POWER_OF_2_OR_ZERO_P (d)
-                        && (rem_flag ? smod_pow2_cheap : sdiv_pow2_cheap))
+                        && (rem_flag ? smod_pow2_cheap : sdiv_pow2_cheap)
+                        /* ??? The cheap metric is computed only for
+                           word_mode.  If this operation is wider, this may
+                           not be so.  Assume true if the optab has an
+                           expander for this mode.  */
+                        && (((rem_flag ? smod_optab : sdiv_optab)
+                             ->handlers[(int) compute_mode].insn_code
+                             != CODE_FOR_nothing)
+                            || (sdivmod_optab->handlers[(int) compute_mode]
+                                .insn_code != CODE_FOR_nothing)))
                  ;
                else if (EXACT_POWER_OF_2_OR_ZERO_P (abs_d))
                  {
@@ -3342,8 +3315,8 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                        t1 = copy_to_mode_reg (compute_mode, op0);
                        do_cmp_and_jump (t1, const0_rtx, GE,
                                         compute_mode, label);
-                       expand_inc (t1, GEN_INT (trunc_int_for_mode
-                                                (abs_d - 1, compute_mode)));
+                       expand_inc (t1, gen_int_mode (abs_d - 1,
+                                                     compute_mode));
                        emit_label (label);
                        quotient = expand_shift (RSHIFT_EXPR, compute_mode, t1,
                                                 build_int_2 (lgup, 0),
@@ -3376,7 +3349,7 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                            && SET_DEST (set) == quotient
                            && abs_d < ((unsigned HOST_WIDE_INT) 1
                                        << (HOST_BITS_PER_WIDE_INT - 1)))
-                         set_unique_reg_note (insn, 
+                         set_unique_reg_note (insn,
                                               REG_EQUAL,
                                               gen_rtx_DIV (compute_mode,
                                                            op0,
@@ -3467,7 +3440,7 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                if (insn != last
                    && (set = single_set (insn)) != 0
                    && SET_DEST (set) == quotient)
-                 set_unique_reg_note (insn, 
+                 set_unique_reg_note (insn,
                                       REG_EQUAL,
                                       gen_rtx_DIV (compute_mode, op0, op1));
              }
@@ -3883,8 +3856,7 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
            t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
                               build_int_2 (pre_shift, 0), NULL_RTX, unsignedp);
            quotient = expand_mult (compute_mode, t1,
-                                   GEN_INT (trunc_int_for_mode
-                                            (ml, compute_mode)),
+                                   gen_int_mode (ml, compute_mode),
                                    NULL_RTX, 0);
 
            insn = get_last_insn ();
@@ -3960,7 +3932,7 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
            emit_label (label);
          }
        return gen_lowpart (mode, rem_flag ? remainder : quotient);
-       
+
       default:
        abort ();
       }
@@ -3973,8 +3945,8 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
       if (rem_flag)
        {
          /* 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
+            If we seem to have a divmod pattern that does not require widening,
+            don't try widening 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
@@ -4065,7 +4037,7 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
 /* Return a tree node with data type TYPE, describing the value of X.
    Usually this is an RTL_EXPR, if there is no obvious better choice.
    X may be an expression, however we only support those expressions
-   generated by loop.c.   */
+   generated by loop.c.  */
 
 tree
 make_tree (type, x)
@@ -4099,42 +4071,61 @@ make_tree (type, x)
        }
 
       return t;
-         
+
+    case CONST_VECTOR:
+      {
+       int i, units;
+       rtx elt;
+       tree t = NULL_TREE;
+
+       units = CONST_VECTOR_NUNITS (x);
+
+       /* Build a tree with vector elements.  */
+       for (i = units - 1; i >= 0; --i)
+         {
+           elt = CONST_VECTOR_ELT (x, i);
+           t = tree_cons (NULL_TREE, make_tree (type, elt), t);
+         }
+
+       return build_vector (type, t);
+      }
+
     case PLUS:
       return fold (build (PLUS_EXPR, type, make_tree (type, XEXP (x, 0)),
                          make_tree (type, XEXP (x, 1))));
-                                                      
+
     case MINUS:
       return fold (build (MINUS_EXPR, type, make_tree (type, XEXP (x, 0)),
                          make_tree (type, XEXP (x, 1))));
-                                                      
+
     case NEG:
       return fold (build1 (NEGATE_EXPR, type, make_tree (type, XEXP (x, 0))));
 
     case MULT:
       return fold (build (MULT_EXPR, type, make_tree (type, XEXP (x, 0)),
                          make_tree (type, XEXP (x, 1))));
-                                                     
+
     case ASHIFT:
       return fold (build (LSHIFT_EXPR, type, make_tree (type, XEXP (x, 0)),
                          make_tree (type, XEXP (x, 1))));
-                                                     
+
     case LSHIFTRT:
+      t = (*lang_hooks.types.unsigned_type) (type);
       return fold (convert (type,
-                           build (RSHIFT_EXPR, unsigned_type (type),
-                                  make_tree (unsigned_type (type),
-                                             XEXP (x, 0)),
+                           build (RSHIFT_EXPR, t,
+                                  make_tree (t, XEXP (x, 0)),
                                   make_tree (type, XEXP (x, 1)))));
-                                                     
+
     case ASHIFTRT:
+      t = (*lang_hooks.types.signed_type) (type);
       return fold (convert (type,
-                           build (RSHIFT_EXPR, signed_type (type),
-                                  make_tree (signed_type (type), XEXP (x, 0)),
+                           build (RSHIFT_EXPR, t,
+                                  make_tree (t, XEXP (x, 0)),
                                   make_tree (type, XEXP (x, 1)))));
-                                                     
+
     case DIV:
       if (TREE_CODE (type) != REAL_TYPE)
-       t = signed_type (type);
+       t = (*lang_hooks.types.signed_type) (type);
       else
        t = type;
 
@@ -4143,11 +4134,18 @@ make_tree (type, x)
                                   make_tree (t, XEXP (x, 0)),
                                   make_tree (t, XEXP (x, 1)))));
     case UDIV:
-      t = unsigned_type (type);
+      t = (*lang_hooks.types.unsigned_type) (type);
       return fold (convert (type,
                            build (TRUNC_DIV_EXPR, t,
                                   make_tree (t, XEXP (x, 0)),
                                   make_tree (t, XEXP (x, 1)))));
+
+    case SIGN_EXTEND:
+    case ZERO_EXTEND:
+      t = (*lang_hooks.types.type_for_mode) (GET_MODE (XEXP (x, 0)),
+                                            GET_CODE (x) == ZERO_EXTEND);
+      return fold (convert (type, make_tree (t, XEXP (x, 0))));
+
    default:
       t = make_node (RTL_EXPR);
       TREE_TYPE (t) = type;
@@ -4167,12 +4165,50 @@ make_tree (type, x)
     }
 }
 
+/* Check whether the multiplication X * MULT + ADD overflows.
+   X, MULT and ADD must be CONST_*.
+   MODE is the machine mode for the computation.
+   X and MULT must have mode MODE.  ADD may have a different mode.
+   So can X (defaults to same as MODE).
+   UNSIGNEDP is nonzero to do unsigned multiplication.  */
+
+bool
+const_mult_add_overflow_p (x, mult, add, mode, unsignedp)
+     rtx x, mult, add;
+     enum machine_mode mode;
+     int unsignedp;
+{
+  tree type, mult_type, add_type, result;
+
+  type = (*lang_hooks.types.type_for_mode) (mode, unsignedp);
+
+  /* In order to get a proper overflow indication from an unsigned
+     type, we have to pretend that it's a sizetype.  */
+  mult_type = type;
+  if (unsignedp)
+    {
+      mult_type = copy_node (type);
+      TYPE_IS_SIZETYPE (mult_type) = 1;
+    }
+
+  add_type = (GET_MODE (add) == VOIDmode ? mult_type
+             : (*lang_hooks.types.type_for_mode) (GET_MODE (add), unsignedp));
+
+  result = fold (build (PLUS_EXPR, mult_type,
+                       fold (build (MULT_EXPR, mult_type,
+                                    make_tree (mult_type, x),
+                                    make_tree (mult_type, mult))),
+                       make_tree (add_type, add)));
+
+  return TREE_CONSTANT_OVERFLOW (result);
+}
+
 /* Return an rtx representing the value of X * MULT + ADD.
    TARGET is a suggestion for where to store the result (an rtx).
    MODE is the machine mode for the computation.
    X and MULT must have mode MODE.  ADD may have a different mode.
    So can X (defaults to same as MODE).
-   UNSIGNEDP is non-zero to do unsigned multiplication.
+   UNSIGNEDP is nonzero to do unsigned multiplication.
    This may emit insns.  */
 
 rtx
@@ -4181,9 +4217,10 @@ expand_mult_add (x, target, mult, add, mode, unsignedp)
      enum machine_mode mode;
      int unsignedp;
 {
-  tree type = type_for_mode (mode, unsignedp);
+  tree type = (*lang_hooks.types.type_for_mode) (mode, unsignedp);
   tree add_type = (GET_MODE (add) == VOIDmode
-                  ? type : type_for_mode (GET_MODE (add), unsignedp));
+                  ? type: (*lang_hooks.types.type_for_mode) (GET_MODE (add),
+                                                             unsignedp));
   tree result =  fold (build (PLUS_EXPR, type,
                              fold (build (MULT_EXPR, type,
                                           make_tree (type, x),
@@ -4199,23 +4236,16 @@ expand_mult_add (x, target, mult, add, mode, unsignedp)
    If TARGET is 0, a pseudo-register or constant is returned.  */
 
 rtx
-expand_and (op0, op1, target)
+expand_and (mode, op0, op1, target)
+     enum machine_mode mode;
      rtx op0, op1, target;
 {
-  enum machine_mode mode = VOIDmode;
-  rtx tem;
-
-  if (GET_MODE (op0) != VOIDmode)
-    mode = GET_MODE (op0);
-  else if (GET_MODE (op1) != VOIDmode)
-    mode = GET_MODE (op1);
+  rtx tem = 0;
 
-  if (mode != VOIDmode)
+  if (GET_MODE (op0) == VOIDmode && GET_MODE (op1) == VOIDmode)
+    tem = simplify_binary_operation (AND, mode, op0, op1);
+  if (tem == 0)
     tem = expand_binop (mode, and_optab, op0, op1, target, 0, OPTAB_LIB_WIDEN);
-  else if (GET_CODE (op0) == CONST_INT && GET_CODE (op1) == CONST_INT)
-    tem = GEN_INT (INTVAL (op0) & INTVAL (op1));
-  else
-    abort ();
 
   if (target == 0)
     target = tem;
@@ -4229,7 +4259,7 @@ expand_and (op0, op1, target)
    Return 0 if that cannot be done.
 
    MODE is the mode to use for OP0 and OP1 should they be CONST_INTs.  If
-   it is VOIDmode, they cannot both be CONST_INT.  
+   it is VOIDmode, they cannot both be CONST_INT.
 
    UNSIGNEDP is for the case where we have to widen the operands
    to perform the operation.  It says to use zero-extension.
@@ -4256,6 +4286,10 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
   rtx last = get_last_insn ();
   rtx pattern, comparison;
 
+  /* ??? Ok to do this and then fail? */
+  op0 = protect_from_queue (op0, 0);
+  op1 = protect_from_queue (op1, 0);
+
   if (unsignedp)
     code = unsigned_condition (code);
 
@@ -4273,7 +4307,7 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
   if (mode == VOIDmode)
     mode = GET_MODE (op0);
 
-  /* For some comparisons with 1 and -1, we can convert this to 
+  /* For some comparisons with 1 and -1, we can convert this to
      comparisons with zero.  This will often produce more opportunities for
      store-flag insns.  */
 
@@ -4311,7 +4345,8 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
      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)
+      && op1 == const0_rtx
+      && (GET_CODE (op0) != MEM || ! MEM_VOLATILE_P (op0)))
     {
       if (code == EQ || code == NE)
        {
@@ -4340,7 +4375,7 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
       && (normalizep || STORE_FLAG_VALUE == 1
          || (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)))))
     {
       subtarget = target;
 
@@ -4392,19 +4427,20 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
       last = get_last_insn ();
 
       comparison
-       = compare_from_rtx (op0, op1, code, unsignedp, mode, NULL_RTX, 0);
+       = compare_from_rtx (op0, op1, code, unsignedp, mode, NULL_RTX);
       if (GET_CODE (comparison) == CONST_INT)
        return (comparison == const0_rtx ? const0_rtx
                : normalizep == 1 ? const1_rtx
                : normalizep == -1 ? constm1_rtx
                : const_true_rtx);
 
-      /* If the code of COMPARISON doesn't match CODE, something is
-        wrong; we can no longer be sure that we have the operation.  
-        We could handle this case, but it should not happen.  */
+      /* The code of COMPARISON may not match CODE if compare_from_rtx
+        decided to swap its operands and reverse the original code.
 
-      if (GET_CODE (comparison) != code)
-       abort ();
+        We know that compare_from_rtx returns either a CONST_INT or
+        a new comparison code, so it is safe to just extract the
+        code from COMPARISON.  */
+      code = GET_CODE (comparison);
 
       /* Get a reference to the target in the proper mode for this insn.  */
       compare_mode = insn_data[(int) icode].operand[0].mode;
@@ -4468,14 +4504,14 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
                                subtarget, normalizep == 1);
          else if (STORE_FLAG_VALUE & 1)
            {
-             op0 = expand_and (op0, const1_rtx, subtarget);
+             op0 = expand_and (compare_mode, op0, const1_rtx, subtarget);
              if (normalizep == -1)
                op0 = expand_unop (compare_mode, neg_optab, op0, op0, 0);
            }
          else
            abort ();
 
-         /* If we were converting to a smaller mode, do the 
+         /* If we were converting to a smaller mode, do the
             conversion now.  */
          if (target_mode != compare_mode)
            {
@@ -4520,7 +4556,7 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
       return tem;
     }
 
-  /* Some other cases we can do are EQ, NE, LE, and GT comparisons with 
+  /* Some other cases we can do are EQ, NE, LE, and GT comparisons with
      the constant zero.  Reject all other comparisons at this point.  Only
      do LE and GT if branches are expensive since they are expensive on
      2-operand machines.  */
@@ -4582,22 +4618,22 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
       tem = expand_binop (mode, sub_optab, tem, op0, subtarget, 0,
                          OPTAB_WIDEN);
     }
-                                   
+
   if (code == EQ || code == NE)
     {
       /* For EQ or NE, one way to do the comparison is to apply an operation
-        that converts the operand into a positive number if it is non-zero
+        that converts the operand into a positive number if it is nonzero
         or zero if it was originally zero.  Then, for EQ, we subtract 1 and
         for NE we negate.  This puts the result in the sign bit.  Then we
-        normalize with a shift, if needed. 
+        normalize with a shift, if needed.
 
         Two operations that can do the above actions are ABS and FFS, so try
         them.  If that doesn't work, and MODE is smaller than a full word,
         we can use zero-extension to the wider mode (an unsigned conversion)
         as the operation.  */
 
-      /* Note that ABS doesn't yield a positive number for INT_MIN, but 
-        that is compensated by the subsequent overflow when subtracting 
+      /* Note that ABS doesn't yield a positive number for INT_MIN, but
+        that is compensated by the subsequent overflow when subtracting
         one / negating.  */
 
       if (abs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
@@ -4692,7 +4728,7 @@ emit_store_flag_force (target, code, op0, op1, mode, unsignedp, normalizep)
 
   emit_move_insn (target, const1_rtx);
   label = gen_label_rtx ();
-  do_compare_rtx_and_jump (op0, op1, code, unsignedp, mode, NULL_RTX, 0,
+  do_compare_rtx_and_jump (op0, op1, code, unsignedp, mode, NULL_RTX,
                           NULL_RTX, label);
 
   emit_move_insn (target, const0_rtx);
@@ -4750,24 +4786,22 @@ do_cmp_and_jump (arg1, arg2, op, mode, label)
             that's the only equality operations we do */
        case EQ:
          if (arg2 != const0_rtx || mode != GET_MODE(arg1))
-           abort();
+           abort ();
          do_jump_by_parts_equality_rtx (arg1, label2, label);
          break;
 
        case NE:
          if (arg2 != const0_rtx || mode != GET_MODE(arg1))
-           abort();
+           abort ();
          do_jump_by_parts_equality_rtx (arg1, label, label2);
          break;
 
        default:
-         abort();
+         abort ();
        }
 
       emit_label (label2);
     }
   else
-    {
-      emit_cmp_and_jump_insns (arg1, arg2, op, NULL_RTX, mode, 0, 0, label);
-    }
+    emit_cmp_and_jump_insns (arg1, arg2, op, NULL_RTX, mode, 0, label);
 }