OSDN Git Service

PR libgcj/23508
[pf3gnuchains/gcc-fork.git] / gcc / expr.c
index e5ed2f6..6cfe2d1 100644 (file)
@@ -1,6 +1,6 @@
 /* Convert tree expression to rtl instructions, for GNU compiler.
    Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-   2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+   2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -16,8 +16,8 @@ for more details.
 
 You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA.  */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
 
 #include "config.h"
 #include "system.h"
@@ -126,7 +126,7 @@ static void move_by_pieces_1 (rtx (*) (rtx, ...), enum machine_mode,
                              struct move_by_pieces *);
 static bool block_move_libcall_safe_for_call_parm (void);
 static bool emit_block_move_via_movmem (rtx, rtx, rtx, unsigned);
-static rtx emit_block_move_via_libcall (rtx, rtx, rtx);
+static rtx emit_block_move_via_libcall (rtx, rtx, rtx, bool);
 static tree emit_block_move_libcall_fn (int);
 static void emit_block_move_via_loop (rtx, rtx, rtx, unsigned);
 static rtx clear_by_pieces_1 (void *, HOST_WIDE_INT, enum machine_mode);
@@ -134,8 +134,7 @@ static void clear_by_pieces (rtx, unsigned HOST_WIDE_INT, unsigned int);
 static void store_by_pieces_1 (struct store_by_pieces *, unsigned int);
 static void store_by_pieces_2 (rtx (*) (rtx, ...), enum machine_mode,
                               struct store_by_pieces *);
-static bool clear_storage_via_clrmem (rtx, rtx, unsigned);
-static rtx clear_storage_via_libcall (rtx, rtx);
+static rtx clear_storage_via_libcall (rtx, rtx, bool);
 static tree clear_storage_libcall_fn (int);
 static rtx compress_float_constant (rtx, rtx);
 static rtx get_subtarget (rtx);
@@ -144,9 +143,8 @@ static void store_constructor_field (rtx, unsigned HOST_WIDE_INT,
                                     tree, tree, int, int);
 static void store_constructor (tree, rtx, int, HOST_WIDE_INT);
 static rtx store_field (rtx, HOST_WIDE_INT, HOST_WIDE_INT, enum machine_mode,
-                       tree, enum machine_mode, int, tree, int);
+                       tree, tree, int);
 
-static unsigned HOST_WIDE_INT highest_pow2_factor (tree);
 static unsigned HOST_WIDE_INT highest_pow2_factor_for_target (tree, tree);
 
 static int is_aligning_offset (tree, tree);
@@ -159,6 +157,7 @@ static void emit_single_push_insn (enum machine_mode, rtx, tree);
 #endif
 static void do_tablejump (rtx, enum machine_mode, rtx, rtx, rtx);
 static rtx const_vector_from_tree (tree);
+static void write_complex_part (rtx, rtx, bool);
 
 /* Record for each mode whether we can move a register directly to or
    from an object of that mode in memory.  If we can't, we won't try
@@ -199,14 +198,39 @@ static bool float_extend_from_mem[NUM_MACHINE_MODES][NUM_MACHINE_MODES];
 /* This array records the insn_code of insns to perform block moves.  */
 enum insn_code movmem_optab[NUM_MACHINE_MODES];
 
-/* This array records the insn_code of insns to perform block clears.  */
-enum insn_code clrmem_optab[NUM_MACHINE_MODES];
+/* This array records the insn_code of insns to perform block sets.  */
+enum insn_code setmem_optab[NUM_MACHINE_MODES];
 
-/* These arrays record the insn_code of two different kinds of insns
+/* These arrays record the insn_code of three different kinds of insns
    to perform block compares.  */
 enum insn_code cmpstr_optab[NUM_MACHINE_MODES];
+enum insn_code cmpstrn_optab[NUM_MACHINE_MODES];
 enum insn_code cmpmem_optab[NUM_MACHINE_MODES];
 
+/* Synchronization primitives.  */
+enum insn_code sync_add_optab[NUM_MACHINE_MODES];
+enum insn_code sync_sub_optab[NUM_MACHINE_MODES];
+enum insn_code sync_ior_optab[NUM_MACHINE_MODES];
+enum insn_code sync_and_optab[NUM_MACHINE_MODES];
+enum insn_code sync_xor_optab[NUM_MACHINE_MODES];
+enum insn_code sync_nand_optab[NUM_MACHINE_MODES];
+enum insn_code sync_old_add_optab[NUM_MACHINE_MODES];
+enum insn_code sync_old_sub_optab[NUM_MACHINE_MODES];
+enum insn_code sync_old_ior_optab[NUM_MACHINE_MODES];
+enum insn_code sync_old_and_optab[NUM_MACHINE_MODES];
+enum insn_code sync_old_xor_optab[NUM_MACHINE_MODES];
+enum insn_code sync_old_nand_optab[NUM_MACHINE_MODES];
+enum insn_code sync_new_add_optab[NUM_MACHINE_MODES];
+enum insn_code sync_new_sub_optab[NUM_MACHINE_MODES];
+enum insn_code sync_new_ior_optab[NUM_MACHINE_MODES];
+enum insn_code sync_new_and_optab[NUM_MACHINE_MODES];
+enum insn_code sync_new_xor_optab[NUM_MACHINE_MODES];
+enum insn_code sync_new_nand_optab[NUM_MACHINE_MODES];
+enum insn_code sync_compare_and_swap[NUM_MACHINE_MODES];
+enum insn_code sync_compare_and_swap_cc[NUM_MACHINE_MODES];
+enum insn_code sync_lock_test_and_set[NUM_MACHINE_MODES];
+enum insn_code sync_lock_release[NUM_MACHINE_MODES];
+
 /* SLOW_UNALIGNED_ACCESS is nonzero if unaligned accesses are very slow.  */
 
 #ifndef SLOW_UNALIGNED_ACCESS
@@ -441,19 +465,27 @@ convert_move (rtx to, rtx from, int unsignedp)
     }
   if (GET_MODE_CLASS (from_mode) == MODE_PARTIAL_INT)
     {
+      rtx new_from;
       enum machine_mode full_mode
        = smallest_mode_for_size (GET_MODE_BITSIZE (from_mode), MODE_INT);
 
       gcc_assert (sext_optab->handlers[full_mode][from_mode].insn_code
                  != CODE_FOR_nothing);
 
-      emit_unop_insn (sext_optab->handlers[full_mode][from_mode].insn_code,
-                     to, from, UNKNOWN);
       if (to_mode == full_mode)
-       return;
+       {
+         emit_unop_insn (sext_optab->handlers[full_mode][from_mode].insn_code,
+                         to, from, UNKNOWN);
+         return;
+       }
+
+      new_from = gen_reg_rtx (full_mode);
+      emit_unop_insn (sext_optab->handlers[full_mode][from_mode].insn_code,
+                     new_from, from, UNKNOWN);
 
       /* else proceed to integer conversions below.  */
       from_mode = full_mode;
+      from = new_from;
     }
 
   /* Now both modes are integers.  */
@@ -610,9 +642,6 @@ convert_move (rtx to, rtx from, int unsignedp)
       if ((code = can_extend_p (to_mode, from_mode, unsignedp))
          != CODE_FOR_nothing)
        {
-         if (flag_force_mem)
-           from = force_not_mem (from);
-
          emit_unop_insn (code, to, from, equiv_code);
          return;
        }
@@ -1123,6 +1152,7 @@ emit_block_move (rtx x, rtx y, rtx size, enum block_op_methods method)
   switch (method)
     {
     case BLOCK_OP_NORMAL:
+    case BLOCK_OP_TAILCALL:
       may_use_call = true;
       break;
 
@@ -1171,7 +1201,8 @@ emit_block_move (rtx x, rtx y, rtx size, enum block_op_methods method)
   else if (emit_block_move_via_movmem (x, y, size, align))
     ;
   else if (may_use_call)
-    retval = emit_block_move_via_libcall (x, y, size);
+    retval = emit_block_move_via_libcall (x, y, size,
+                                         method == BLOCK_OP_TAILCALL);
   else
     emit_block_move_via_loop (x, y, size, align);
 
@@ -1219,8 +1250,7 @@ block_move_libcall_safe_for_call_parm (void)
        rtx tmp = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
        if (!tmp || !REG_P (tmp))
          return false;
-       if (FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode,
-                                       NULL_TREE, 1))
+       if (targetm.calls.arg_partial_bytes (&args_so_far, mode, NULL, 1))
          return false;
        FUNCTION_ARG_ADVANCE (args_so_far, mode, NULL_TREE, 1);
       }
@@ -1301,7 +1331,7 @@ emit_block_move_via_movmem (rtx x, rtx y, rtx size, unsigned int align)
    Return the return value from memcpy, 0 otherwise.  */
 
 static rtx
-emit_block_move_via_libcall (rtx dst, rtx src, rtx size)
+emit_block_move_via_libcall (rtx dst, rtx src, rtx size, bool tailcall)
 {
   rtx dst_addr, src_addr;
   tree call_expr, arg_list, fn, src_tree, dst_tree, size_tree;
@@ -1343,6 +1373,7 @@ emit_block_move_via_libcall (rtx dst, rtx src, rtx size)
   call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
   call_expr = build3 (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
                      call_expr, arg_list, NULL_TREE);
+  CALL_EXPR_TAILCALL (call_expr) = tailcall;
 
   retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
 
@@ -1557,19 +1588,39 @@ gen_group_rtx (rtx orig)
   return gen_rtx_PARALLEL (GET_MODE (orig), gen_rtvec_v (length, tmps));
 }
 
-/* Emit code to move a block ORIG_SRC of type TYPE to a block DST,
-   where DST is non-consecutive registers represented by a PARALLEL.
-   SSIZE represents the total size of block ORIG_SRC in bytes, or -1
-   if not known.  */
+/* A subroutine of emit_group_load.  Arguments as for emit_group_load,
+   except that values are placed in TMPS[i], and must later be moved
+   into corresponding XEXP (XVECEXP (DST, 0, i), 0) element.  */
 
-void
-emit_group_load (rtx dst, rtx orig_src, tree type ATTRIBUTE_UNUSED, int ssize)
+static void
+emit_group_load_1 (rtx *tmps, rtx dst, rtx orig_src, tree type, int ssize)
 {
-  rtx *tmps, src;
+  rtx src;
   int start, i;
+  enum machine_mode m = GET_MODE (orig_src);
 
   gcc_assert (GET_CODE (dst) == PARALLEL);
 
+  if (m != VOIDmode
+      && !SCALAR_INT_MODE_P (m)
+      && !MEM_P (orig_src)
+      && GET_CODE (orig_src) != CONCAT)
+    {
+      enum machine_mode imode = int_mode_for_mode (GET_MODE (orig_src));
+      if (imode == BLKmode)
+       src = assign_stack_temp (GET_MODE (orig_src), ssize, 0);
+      else
+       src = gen_reg_rtx (imode);
+      if (imode != BLKmode)
+       src = gen_lowpart (GET_MODE (orig_src), src);
+      emit_move_insn (src, orig_src);
+      /* ...and back again.  */
+      if (imode != BLKmode)
+       src = gen_lowpart (imode, src);
+      emit_group_load_1 (tmps, dst, src, type, ssize);
+      return;
+    }
+
   /* Check for a NULL entry, used to indicate that the parameter goes
      both on the stack and in registers.  */
   if (XEXP (XVECEXP (dst, 0, 0), 0))
@@ -1577,8 +1628,6 @@ emit_group_load (rtx dst, rtx orig_src, tree type ATTRIBUTE_UNUSED, int ssize)
   else
     start = 1;
 
-  tmps = alloca (sizeof (rtx) * XVECLEN (dst, 0));
-
   /* Process the pieces.  */
   for (i = start; i < XVECLEN (dst, 0); i++)
     {
@@ -1632,6 +1681,11 @@ emit_group_load (rtx dst, rtx orig_src, tree type ATTRIBUTE_UNUSED, int ssize)
          tmps[i] = gen_reg_rtx (mode);
          emit_move_insn (tmps[i], adjust_address (src, mode, bytepos));
        }
+      else if (COMPLEX_MODE_P (mode)
+              && GET_MODE (src) == mode
+              && bytelen == GET_MODE_SIZE (mode))
+       /* Let emit_move_complex do the bulk of the work.  */
+       tmps[i] = src;
       else if (GET_CODE (src) == CONCAT)
        {
          unsigned int slen = GET_MODE_SIZE (GET_MODE (src));
@@ -1654,11 +1708,12 @@ emit_group_load (rtx dst, rtx orig_src, tree type ATTRIBUTE_UNUSED, int ssize)
          else
            {
              rtx mem;
-             
+
              gcc_assert (!bytepos);
              mem = assign_stack_temp (GET_MODE (src), slen, 0);
              emit_move_insn (mem, src);
-             tmps[i] = adjust_address (mem, mode, 0);
+             tmps[i] = extract_bit_field (mem, bytelen * BITS_PER_UNIT,
+                                          0, 1, NULL_RTX, mode, mode);
            }
        }
       /* FIXME: A SIMD parallel will eventually lead to a subreg of a
@@ -1689,10 +1744,61 @@ emit_group_load (rtx dst, rtx orig_src, tree type ATTRIBUTE_UNUSED, int ssize)
        tmps[i] = expand_shift (LSHIFT_EXPR, mode, tmps[i],
                                build_int_cst (NULL_TREE, shift), tmps[i], 0);
     }
+}
+
+/* Emit code to move a block SRC of type TYPE to a block DST,
+   where DST is non-consecutive registers represented by a PARALLEL.
+   SSIZE represents the total size of block ORIG_SRC in bytes, or -1
+   if not known.  */
+
+void
+emit_group_load (rtx dst, rtx src, tree type, int ssize)
+{
+  rtx *tmps;
+  int i;
+
+  tmps = alloca (sizeof (rtx) * XVECLEN (dst, 0));
+  emit_group_load_1 (tmps, dst, src, type, ssize);
 
   /* Copy the extracted pieces into the proper (probable) hard regs.  */
-  for (i = start; i < XVECLEN (dst, 0); i++)
-    emit_move_insn (XEXP (XVECEXP (dst, 0, i), 0), tmps[i]);
+  for (i = 0; i < XVECLEN (dst, 0); i++)
+    {
+      rtx d = XEXP (XVECEXP (dst, 0, i), 0);
+      if (d == NULL)
+       continue;
+      emit_move_insn (d, tmps[i]);
+    }
+}
+
+/* Similar, but load SRC into new pseudos in a format that looks like
+   PARALLEL.  This can later be fed to emit_group_move to get things
+   in the right place.  */
+
+rtx
+emit_group_load_into_temps (rtx parallel, rtx src, tree type, int ssize)
+{
+  rtvec vec;
+  int i;
+
+  vec = rtvec_alloc (XVECLEN (parallel, 0));
+  emit_group_load_1 (&RTVEC_ELT (vec, 0), parallel, src, type, ssize);
+
+  /* Convert the vector to look just like the original PARALLEL, except
+     with the computed values.  */
+  for (i = 0; i < XVECLEN (parallel, 0); i++)
+    {
+      rtx e = XVECEXP (parallel, 0, i);
+      rtx d = XEXP (e, 0);
+
+      if (d)
+       {
+         d = force_reg (GET_MODE (d), RTVEC_ELT (vec, i));
+         e = alloc_EXPR_LIST (REG_NOTE_KIND (e), d, XEXP (e, 1));
+       }
+      RTVEC_ELT (vec, i) = e;
+    }
+
+  return gen_rtx_PARALLEL (GET_MODE (parallel), vec);
 }
 
 /* Emit code to move a block SRC to block DST, where SRC and DST are
@@ -1713,6 +1819,27 @@ emit_group_move (rtx dst, rtx src)
                    XEXP (XVECEXP (src, 0, i), 0));
 }
 
+/* Move a group of registers represented by a PARALLEL into pseudos.  */
+
+rtx
+emit_group_move_into_temps (rtx src)
+{
+  rtvec vec = rtvec_alloc (XVECLEN (src, 0));
+  int i;
+
+  for (i = 0; i < XVECLEN (src, 0); i++)
+    {
+      rtx e = XVECEXP (src, 0, i);
+      rtx d = XEXP (e, 0);
+
+      if (d)
+       e = alloc_EXPR_LIST (REG_NOTE_KIND (e), copy_to_reg (d), XEXP (e, 1));
+      RTVEC_ELT (vec, i) = e;
+    }
+
+  return gen_rtx_PARALLEL (GET_MODE (src), vec);
+}
+
 /* Emit code to move a block SRC to a block ORIG_DST of type TYPE,
    where SRC is non-consecutive registers represented by a PARALLEL.
    SSIZE represents the total size of block ORIG_DST, or -1 if not
@@ -1723,9 +1850,25 @@ emit_group_store (rtx orig_dst, rtx src, tree type ATTRIBUTE_UNUSED, int ssize)
 {
   rtx *tmps, dst;
   int start, i;
+  enum machine_mode m = GET_MODE (orig_dst);
 
   gcc_assert (GET_CODE (src) == PARALLEL);
 
+  if (!SCALAR_INT_MODE_P (m)
+      && !MEM_P (orig_dst) && GET_CODE (orig_dst) != CONCAT)
+    {
+      enum machine_mode imode = int_mode_for_mode (GET_MODE (orig_dst));
+      if (imode == BLKmode)
+        dst = assign_stack_temp (GET_MODE (orig_dst), ssize, 0);
+      else
+        dst = gen_reg_rtx (imode);
+      emit_group_store (dst, src, type, ssize);
+      if (imode != BLKmode)
+        dst = gen_lowpart (GET_MODE (orig_dst), dst);
+      emit_move_insn (orig_dst, dst);
+      return;
+    }
+
   /* Check for a NULL entry, used to indicate that the parameter goes
      both on the stack and in registers.  */
   if (XEXP (XVECEXP (src, 0, 0), 0))
@@ -2291,95 +2434,60 @@ store_by_pieces_2 (rtx (*genfun) (rtx, ...), enum machine_mode mode,
    its length in bytes.  */
 
 rtx
-clear_storage (rtx object, rtx size)
+clear_storage (rtx object, rtx size, enum block_op_methods method)
 {
-  rtx retval = 0;
-  unsigned int align = (MEM_P (object) ? MEM_ALIGN (object)
-                       : GET_MODE_ALIGNMENT (GET_MODE (object)));
+  enum machine_mode mode = GET_MODE (object);
+  unsigned int align;
+
+  gcc_assert (method == BLOCK_OP_NORMAL || method == BLOCK_OP_TAILCALL);
 
   /* If OBJECT is not BLKmode and SIZE is the same size as its mode,
      just move a zero.  Otherwise, do this a piece at a time.  */
-  if (GET_MODE (object) != BLKmode
+  if (mode != BLKmode
       && GET_CODE (size) == CONST_INT
-      && INTVAL (size) == (HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (object)))
-    emit_move_insn (object, CONST0_RTX (GET_MODE (object)));
-  else
-    {
-      if (size == const0_rtx)
-       ;
-      else if (GET_CODE (size) == CONST_INT
-         && CLEAR_BY_PIECES_P (INTVAL (size), align))
-       clear_by_pieces (object, INTVAL (size), align);
-      else if (clear_storage_via_clrmem (object, size, align))
-       ;
-      else
-       retval = clear_storage_via_libcall (object, size);
-    }
-
-  return retval;
-}
-
-/* A subroutine of clear_storage.  Expand a clrmem pattern;
-   return true if successful.  */
-
-static bool
-clear_storage_via_clrmem (rtx object, rtx size, unsigned int align)
-{
-  /* Try the most limited insn first, because there's no point
-     including more than one in the machine description unless
-     the more limited one has some advantage.  */
-
-  rtx opalign = GEN_INT (align / BITS_PER_UNIT);
-  enum machine_mode mode;
-
-  for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
-       mode = GET_MODE_WIDER_MODE (mode))
+      && INTVAL (size) == (HOST_WIDE_INT) GET_MODE_SIZE (mode))
     {
-      enum insn_code code = clrmem_optab[(int) mode];
-      insn_operand_predicate_fn pred;
-
-      if (code != CODE_FOR_nothing
-         /* We don't need MODE to be narrower than
-            BITS_PER_HOST_WIDE_INT here because if SIZE is less than
-            the mode mask, as it is returned by the macro, it will
-            definitely be less than the actual mode mask.  */
-         && ((GET_CODE (size) == CONST_INT
-              && ((unsigned HOST_WIDE_INT) INTVAL (size)
-                  <= (GET_MODE_MASK (mode) >> 1)))
-             || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD)
-         && ((pred = insn_data[(int) code].operand[0].predicate) == 0
-             || (*pred) (object, BLKmode))
-         && ((pred = insn_data[(int) code].operand[2].predicate) == 0
-             || (*pred) (opalign, VOIDmode)))
+      rtx zero = CONST0_RTX (mode);
+      if (zero != NULL)
        {
-         rtx op1;
-         rtx last = get_last_insn ();
-         rtx pat;
-
-         op1 = convert_to_mode (mode, size, 1);
-         pred = insn_data[(int) code].operand[1].predicate;
-         if (pred != 0 && ! (*pred) (op1, mode))
-           op1 = copy_to_mode_reg (mode, op1);
+         emit_move_insn (object, zero);
+         return NULL;
+       }
 
-         pat = GEN_FCN ((int) code) (object, op1, opalign);
-         if (pat)
+      if (COMPLEX_MODE_P (mode))
+       {
+         zero = CONST0_RTX (GET_MODE_INNER (mode));
+         if (zero != NULL)
            {
-             emit_insn (pat);
-             return true;
+             write_complex_part (object, zero, 0);
+             write_complex_part (object, zero, 1);
+             return NULL;
            }
-         else
-           delete_insns_since (last);
        }
     }
 
-  return false;
+  if (size == const0_rtx)
+    return NULL;
+
+  align = MEM_ALIGN (object);
+
+  if (GET_CODE (size) == CONST_INT
+      && CLEAR_BY_PIECES_P (INTVAL (size), align))
+    clear_by_pieces (object, INTVAL (size), align);
+  else if (set_storage_via_setmem (object, size, const0_rtx, align))
+    ;
+  else
+    return clear_storage_via_libcall (object, size,
+                                     method == BLOCK_OP_TAILCALL);
+
+  return NULL;
 }
 
 /* A subroutine of clear_storage.  Expand a call to memset.
    Return the return value of memset, 0 otherwise.  */
 
 static rtx
-clear_storage_via_libcall (rtx object, rtx size)
+clear_storage_via_libcall (rtx object, rtx size, bool tailcall)
 {
   tree call_expr, arg_list, fn, object_tree, size_tree;
   enum machine_mode size_mode;
@@ -2412,6 +2520,7 @@ clear_storage_via_libcall (rtx object, rtx size)
   call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
   call_expr = build3 (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
                      call_expr, arg_list, NULL_TREE);
+  CALL_EXPR_TAILCALL (call_expr) = tailcall;
 
   retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
 
@@ -2467,417 +2576,621 @@ clear_storage_libcall_fn (int for_call)
   return block_clear_fn;
 }
 \f
-/* Generate code to copy Y into X.
-   Both Y and X must have the same mode, except that
-   Y can be a constant with VOIDmode.
-   This mode cannot be BLKmode; use emit_block_move for that.
+/* Expand a setmem pattern; return true if successful.  */
 
-   Return the last instruction emitted.  */
-
-rtx
-emit_move_insn (rtx x, rtx y)
+bool
+set_storage_via_setmem (rtx object, rtx size, rtx val, unsigned int align)
 {
-  enum machine_mode mode = GET_MODE (x);
-  rtx y_cst = NULL_RTX;
-  rtx last_insn, set;
+  /* Try the most limited insn first, because there's no point
+     including more than one in the machine description unless
+     the more limited one has some advantage.  */
 
-  gcc_assert (mode != BLKmode
-             && (GET_MODE (y) == mode || GET_MODE (y) == VOIDmode));
+  rtx opalign = GEN_INT (align / BITS_PER_UNIT);
+  enum machine_mode mode;
 
-  if (CONSTANT_P (y))
+  for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
+       mode = GET_MODE_WIDER_MODE (mode))
     {
-      if (optimize
-         && SCALAR_FLOAT_MODE_P (GET_MODE (x))
-         && (last_insn = compress_float_constant (x, y)))
-       return last_insn;
-
-      y_cst = y;
+      enum insn_code code = setmem_optab[(int) mode];
+      insn_operand_predicate_fn pred;
 
-      if (!LEGITIMATE_CONSTANT_P (y))
+      if (code != CODE_FOR_nothing
+         /* We don't need MODE to be narrower than
+            BITS_PER_HOST_WIDE_INT here because if SIZE is less than
+            the mode mask, as it is returned by the macro, it will
+            definitely be less than the actual mode mask.  */
+         && ((GET_CODE (size) == CONST_INT
+              && ((unsigned HOST_WIDE_INT) INTVAL (size)
+                  <= (GET_MODE_MASK (mode) >> 1)))
+             || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD)
+         && ((pred = insn_data[(int) code].operand[0].predicate) == 0
+             || (*pred) (object, BLKmode))
+         && ((pred = insn_data[(int) code].operand[3].predicate) == 0
+             || (*pred) (opalign, VOIDmode)))
        {
-         y = force_const_mem (mode, y);
+         rtx opsize, opchar;
+         enum machine_mode char_mode;
+         rtx last = get_last_insn ();
+         rtx pat;
 
-         /* If the target's cannot_force_const_mem prevented the spill,
-            assume that the target's move expanders will also take care
-            of the non-legitimate constant.  */
-         if (!y)
-           y = y_cst;
+         opsize = convert_to_mode (mode, size, 1);
+         pred = insn_data[(int) code].operand[1].predicate;
+         if (pred != 0 && ! (*pred) (opsize, mode))
+           opsize = copy_to_mode_reg (mode, opsize);
+         
+         opchar = val;
+         char_mode = insn_data[(int) code].operand[2].mode;
+         if (char_mode != VOIDmode)
+           {
+             opchar = convert_to_mode (char_mode, opchar, 1);
+             pred = insn_data[(int) code].operand[2].predicate;
+             if (pred != 0 && ! (*pred) (opchar, char_mode))
+               opchar = copy_to_mode_reg (char_mode, opchar);
+           }
+
+         pat = GEN_FCN ((int) code) (object, opsize, opchar, opalign);
+         if (pat)
+           {
+             emit_insn (pat);
+             return true;
+           }
+         else
+           delete_insns_since (last);
        }
     }
 
-  /* If X or Y are memory references, verify that their addresses are valid
-     for the machine.  */
-  if (MEM_P (x)
-      && ((! memory_address_p (GET_MODE (x), XEXP (x, 0))
-          && ! push_operand (x, GET_MODE (x)))
-         || (flag_force_addr
-             && CONSTANT_ADDRESS_P (XEXP (x, 0)))))
-    x = validize_mem (x);
+  return false;
+}
 
-  if (MEM_P (y)
-      && (! memory_address_p (GET_MODE (y), XEXP (y, 0))
-         || (flag_force_addr
-             && CONSTANT_ADDRESS_P (XEXP (y, 0)))))
-    y = validize_mem (y);
+\f
+/* Write to one of the components of the complex value CPLX.  Write VAL to
+   the real part if IMAG_P is false, and the imaginary part if its true.  */
 
-  gcc_assert (mode != BLKmode);
+static void
+write_complex_part (rtx cplx, rtx val, bool imag_p)
+{
+  enum machine_mode cmode;
+  enum machine_mode imode;
+  unsigned ibitsize;
 
-  last_insn = emit_move_insn_1 (x, y);
+  if (GET_CODE (cplx) == CONCAT)
+    {
+      emit_move_insn (XEXP (cplx, imag_p), val);
+      return;
+    }
 
-  if (y_cst && REG_P (x)
-      && (set = single_set (last_insn)) != NULL_RTX
-      && SET_DEST (set) == x
-      && ! rtx_equal_p (y_cst, SET_SRC (set)))
-    set_unique_reg_note (last_insn, REG_EQUAL, y_cst);
+  cmode = GET_MODE (cplx);
+  imode = GET_MODE_INNER (cmode);
+  ibitsize = GET_MODE_BITSIZE (imode);
 
-  return last_insn;
+  /* For MEMs simplify_gen_subreg may generate an invalid new address
+     because, e.g., the original address is considered mode-dependent
+     by the target, which restricts simplify_subreg from invoking
+     adjust_address_nv.  Instead of preparing fallback support for an
+     invalid address, we call adjust_address_nv directly.  */
+  if (MEM_P (cplx))
+    {
+      emit_move_insn (adjust_address_nv (cplx, imode,
+                                        imag_p ? GET_MODE_SIZE (imode) : 0),
+                     val);
+      return;
+    }
+
+  /* If the sub-object is at least word sized, then we know that subregging
+     will work.  This special case is important, since store_bit_field
+     wants to operate on integer modes, and there's rarely an OImode to
+     correspond to TCmode.  */
+  if (ibitsize >= BITS_PER_WORD
+      /* For hard regs we have exact predicates.  Assume we can split
+        the original object if it spans an even number of hard regs.
+        This special case is important for SCmode on 64-bit platforms
+        where the natural size of floating-point regs is 32-bit.  */
+      || (REG_P (cplx)
+         && REGNO (cplx) < FIRST_PSEUDO_REGISTER
+         && hard_regno_nregs[REGNO (cplx)][cmode] % 2 == 0))
+    {
+      rtx part = simplify_gen_subreg (imode, cplx, cmode,
+                                     imag_p ? GET_MODE_SIZE (imode) : 0);
+      if (part)
+        {
+         emit_move_insn (part, val);
+         return;
+       }
+      else
+       /* simplify_gen_subreg may fail for sub-word MEMs.  */
+       gcc_assert (MEM_P (cplx) && ibitsize < BITS_PER_WORD);
+    }
+
+  store_bit_field (cplx, ibitsize, imag_p ? ibitsize : 0, imode, val);
 }
 
-/* Low level part of emit_move_insn.
-   Called just like emit_move_insn, but assumes X and Y
-   are basically valid.  */
+/* Extract one of the components of the complex value CPLX.  Extract the
+   real part if IMAG_P is false, and the imaginary part if it's true.  */
 
-rtx
-emit_move_insn_1 (rtx x, rtx y)
+static rtx
+read_complex_part (rtx cplx, bool imag_p)
 {
-  enum machine_mode mode = GET_MODE (x);
-  enum machine_mode submode;
-  enum mode_class class = GET_MODE_CLASS (mode);
+  enum machine_mode cmode, imode;
+  unsigned ibitsize;
 
-  gcc_assert ((unsigned int) mode < (unsigned int) MAX_MACHINE_MODE);
+  if (GET_CODE (cplx) == CONCAT)
+    return XEXP (cplx, imag_p);
 
-  if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
-    return
-      emit_insn (GEN_FCN (mov_optab->handlers[(int) mode].insn_code) (x, y));
+  cmode = GET_MODE (cplx);
+  imode = GET_MODE_INNER (cmode);
+  ibitsize = GET_MODE_BITSIZE (imode);
 
-  /* Expand complex moves by moving real part and imag part, if possible.  */
-  else if ((class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT)
-          && BLKmode != (submode = GET_MODE_INNER (mode))
-          && (mov_optab->handlers[(int) submode].insn_code
-              != CODE_FOR_nothing))
+  /* Special case reads from complex constants that got spilled to memory.  */
+  if (MEM_P (cplx) && GET_CODE (XEXP (cplx, 0)) == SYMBOL_REF)
     {
-      /* Don't split destination if it is a stack push.  */
-      int stack = push_operand (x, GET_MODE (x));
-
-#ifdef PUSH_ROUNDING
-      /* In case we output to the stack, but the size is smaller than the
-        machine can push exactly, we need to use move instructions.  */
-      if (stack
-         && (PUSH_ROUNDING (GET_MODE_SIZE (submode))
-             != GET_MODE_SIZE (submode)))
+      tree decl = SYMBOL_REF_DECL (XEXP (cplx, 0));
+      if (decl && TREE_CODE (decl) == COMPLEX_CST)
        {
-         rtx temp;
-         HOST_WIDE_INT offset1, offset2;
+         tree part = imag_p ? TREE_IMAGPART (decl) : TREE_REALPART (decl);
+         if (CONSTANT_CLASS_P (part))
+           return expand_expr (part, NULL_RTX, imode, EXPAND_NORMAL);
+       }
+    }
 
-         /* Do not use anti_adjust_stack, since we don't want to update
-            stack_pointer_delta.  */
-         temp = expand_binop (Pmode,
-#ifdef STACK_GROWS_DOWNWARD
-                              sub_optab,
-#else
-                              add_optab,
-#endif
-                              stack_pointer_rtx,
-                              GEN_INT
-                                (PUSH_ROUNDING
-                                 (GET_MODE_SIZE (GET_MODE (x)))),
-                              stack_pointer_rtx, 0, OPTAB_LIB_WIDEN);
+  /* For MEMs simplify_gen_subreg may generate an invalid new address
+     because, e.g., the original address is considered mode-dependent
+     by the target, which restricts simplify_subreg from invoking
+     adjust_address_nv.  Instead of preparing fallback support for an
+     invalid address, we call adjust_address_nv directly.  */
+  if (MEM_P (cplx))
+    return adjust_address_nv (cplx, imode,
+                             imag_p ? GET_MODE_SIZE (imode) : 0);
+
+  /* If the sub-object is at least word sized, then we know that subregging
+     will work.  This special case is important, since extract_bit_field
+     wants to operate on integer modes, and there's rarely an OImode to
+     correspond to TCmode.  */
+  if (ibitsize >= BITS_PER_WORD
+      /* For hard regs we have exact predicates.  Assume we can split
+        the original object if it spans an even number of hard regs.
+        This special case is important for SCmode on 64-bit platforms
+        where the natural size of floating-point regs is 32-bit.  */
+      || (REG_P (cplx)
+         && REGNO (cplx) < FIRST_PSEUDO_REGISTER
+         && hard_regno_nregs[REGNO (cplx)][cmode] % 2 == 0))
+    {
+      rtx ret = simplify_gen_subreg (imode, cplx, cmode,
+                                    imag_p ? GET_MODE_SIZE (imode) : 0);
+      if (ret)
+        return ret;
+      else
+       /* simplify_gen_subreg may fail for sub-word MEMs.  */
+       gcc_assert (MEM_P (cplx) && ibitsize < BITS_PER_WORD);
+    }
 
-         if (temp != stack_pointer_rtx)
-           emit_move_insn (stack_pointer_rtx, temp);
+  return extract_bit_field (cplx, ibitsize, imag_p ? ibitsize : 0,
+                           true, NULL_RTX, imode, imode);
+}
+\f
+/* A subroutine of emit_move_insn_1.  Yet another lowpart generator.
+   NEW_MODE and OLD_MODE are the same size.  Return NULL if X cannot be
+   represented in NEW_MODE.  If FORCE is true, this will never happen, as
+   we'll force-create a SUBREG if needed.  */
 
-#ifdef STACK_GROWS_DOWNWARD
-         offset1 = 0;
-         offset2 = GET_MODE_SIZE (submode);
-#else
-         offset1 = -PUSH_ROUNDING (GET_MODE_SIZE (GET_MODE (x)));
-         offset2 = (-PUSH_ROUNDING (GET_MODE_SIZE (GET_MODE (x)))
-                    + GET_MODE_SIZE (submode));
-#endif
+static rtx
+emit_move_change_mode (enum machine_mode new_mode,
+                      enum machine_mode old_mode, rtx x, bool force)
+{
+  rtx ret;
 
-         emit_move_insn (change_address (x, submode,
-                                         gen_rtx_PLUS (Pmode,
-                                                       stack_pointer_rtx,
-                                                       GEN_INT (offset1))),
-                         gen_realpart (submode, y));
-         emit_move_insn (change_address (x, submode,
-                                         gen_rtx_PLUS (Pmode,
-                                                       stack_pointer_rtx,
-                                                       GEN_INT (offset2))),
-                         gen_imagpart (submode, y));
-       }
-      else
-#endif
-      /* If this is a stack, push the highpart first, so it
-        will be in the argument order.
+  if (reload_in_progress && MEM_P (x))
+    {
+      /* We can't use gen_lowpart here because it may call change_address
+        which is not appropriate if we were called when a reload was in
+        progress.  We don't have to worry about changing the address since
+        the size in bytes is supposed to be the same.  Copy the MEM to
+        change the mode and move any substitutions from the old MEM to
+        the new one.  */
 
-        In that case, change_address is used only to convert
-        the mode, not to change the address.  */
-      if (stack)
-       {
-         /* Note that the real part always precedes the imag part in memory
-            regardless of machine's endianness.  */
-#ifdef STACK_GROWS_DOWNWARD
-         emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
-                         gen_imagpart (submode, y));
-         emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
-                         gen_realpart (submode, y));
-#else
-         emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
-                         gen_realpart (submode, y));
-         emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
-                         gen_imagpart (submode, y));
-#endif
-       }
+      ret = adjust_address_nv (x, new_mode, 0);
+      copy_replacements (x, ret);
+    }
+  else
+    {
+      /* Note that we do want simplify_subreg's behavior of validating
+        that the new mode is ok for a hard register.  If we were to use
+        simplify_gen_subreg, we would create the subreg, but would
+        probably run into the target not being able to implement it.  */
+      /* Except, of course, when FORCE is true, when this is exactly what
+        we want.  Which is needed for CCmodes on some targets.  */
+      if (force)
+       ret = simplify_gen_subreg (new_mode, x, old_mode, 0);
       else
-       {
-         rtx realpart_x, realpart_y;
-         rtx imagpart_x, imagpart_y;
-
-         /* If this is a complex value with each part being smaller than a
-            word, the usual calling sequence will likely pack the pieces into
-            a single register.  Unfortunately, SUBREG of hard registers only
-            deals in terms of words, so we have a problem converting input
-            arguments to the CONCAT of two registers that is used elsewhere
-            for complex values.  If this is before reload, we can copy it into
-            memory and reload.  FIXME, we should see about using extract and
-            insert on integer registers, but complex short and complex char
-            variables should be rarely used.  */
-         if (GET_MODE_BITSIZE (mode) < 2 * BITS_PER_WORD
-             && (reload_in_progress | reload_completed) == 0)
-           {
-             int packed_dest_p
-               = (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER);
-             int packed_src_p
-               = (REG_P (y) && REGNO (y) < FIRST_PSEUDO_REGISTER);
+       ret = simplify_subreg (new_mode, x, old_mode, 0);
+    }
 
-             if (packed_dest_p || packed_src_p)
-               {
-                 enum mode_class reg_class = ((class == MODE_COMPLEX_FLOAT)
-                                              ? MODE_FLOAT : MODE_INT);
+  return ret;
+}
+
+/* A subroutine of emit_move_insn_1.  Generate a move from Y into X using
+   an integer mode of the same size as MODE.  Returns the instruction
+   emitted, or NULL if such a move could not be generated.  */
+
+static rtx
+emit_move_via_integer (enum machine_mode mode, rtx x, rtx y)
+{
+  enum machine_mode imode;
+  enum insn_code code;
 
-                 enum machine_mode reg_mode
-                   = mode_for_size (GET_MODE_BITSIZE (mode), reg_class, 1);
+  /* There must exist a mode of the exact size we require.  */
+  imode = int_mode_for_mode (mode);
+  if (imode == BLKmode)
+    return NULL_RTX;
 
-                 if (reg_mode != BLKmode)
-                   {
-                     rtx mem = assign_stack_temp (reg_mode,
-                                                  GET_MODE_SIZE (mode), 0);
-                     rtx cmem = adjust_address (mem, mode, 0);
+  /* The target must support moves in this mode.  */
+  code = mov_optab->handlers[imode].insn_code;
+  if (code == CODE_FOR_nothing)
+    return NULL_RTX;
 
-                     if (packed_dest_p)
-                       {
-                         rtx sreg = gen_rtx_SUBREG (reg_mode, x, 0);
+  x = emit_move_change_mode (imode, mode, x, false);
+  if (x == NULL_RTX)
+    return NULL_RTX;
+  y = emit_move_change_mode (imode, mode, y, false);
+  if (y == NULL_RTX)
+    return NULL_RTX;
+  return emit_insn (GEN_FCN (code) (x, y));
+}
 
-                         emit_move_insn_1 (cmem, y);
-                         return emit_move_insn_1 (sreg, mem);
-                       }
-                     else
-                       {
-                         rtx sreg = gen_rtx_SUBREG (reg_mode, y, 0);
+/* A subroutine of emit_move_insn_1.  X is a push_operand in MODE.
+   Return an equivalent MEM that does not use an auto-increment.  */
 
-                         emit_move_insn_1 (mem, sreg);
-                         return emit_move_insn_1 (x, cmem);
-                       }
-                   }
-               }
-           }
+static rtx
+emit_move_resolve_push (enum machine_mode mode, rtx x)
+{
+  enum rtx_code code = GET_CODE (XEXP (x, 0));
+  HOST_WIDE_INT adjust;
+  rtx temp;
 
-         realpart_x = gen_realpart (submode, x);
-         realpart_y = gen_realpart (submode, y);
-         imagpart_x = gen_imagpart (submode, x);
-         imagpart_y = gen_imagpart (submode, y);
-
-         /* Show the output dies here.  This is necessary for SUBREGs
-            of pseudos since we cannot track their lifetimes correctly;
-            hard regs shouldn't appear here except as return values.
-            We never want to emit such a clobber after reload.  */
-         if (x != y
-             && ! (reload_in_progress || reload_completed)
-             && (GET_CODE (realpart_x) == SUBREG
-                 || GET_CODE (imagpart_x) == SUBREG))
-           emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
-
-         emit_move_insn (realpart_x, realpart_y);
-         emit_move_insn (imagpart_x, imagpart_y);
-       }
+  adjust = GET_MODE_SIZE (mode);
+#ifdef PUSH_ROUNDING
+  adjust = PUSH_ROUNDING (adjust);
+#endif
+  if (code == PRE_DEC || code == POST_DEC)
+    adjust = -adjust;
 
-      return get_last_insn ();
+  /* Do not use anti_adjust_stack, since we don't want to update
+     stack_pointer_delta.  */
+  temp = expand_simple_binop (Pmode, PLUS, stack_pointer_rtx,
+                             GEN_INT (adjust), stack_pointer_rtx,
+                             0, OPTAB_LIB_WIDEN);
+  if (temp != stack_pointer_rtx)
+    emit_move_insn (stack_pointer_rtx, temp);
+
+  switch (code)
+    {
+    case PRE_INC:
+    case PRE_DEC:
+      temp = stack_pointer_rtx;
+      break;
+    case POST_INC:
+      temp = plus_constant (stack_pointer_rtx, -GET_MODE_SIZE (mode));
+      break;
+    case POST_DEC:
+      temp = plus_constant (stack_pointer_rtx, GET_MODE_SIZE (mode));
+      break;
+    default:
+      gcc_unreachable ();
     }
 
-  /* Handle MODE_CC modes:  If we don't have a special move insn for this mode,
-     find a mode to do it in.  If we have a movcc, use it.  Otherwise,
-     find the MODE_INT mode of the same width.  */
-  else if (GET_MODE_CLASS (mode) == MODE_CC
-          && mov_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+  return replace_equiv_address (x, temp);
+}
+
+/* A subroutine of emit_move_complex.  Generate a move from Y into X.
+   X is known to satisfy push_operand, and MODE is known to be complex.
+   Returns the last instruction emitted.  */
+
+static rtx
+emit_move_complex_push (enum machine_mode mode, rtx x, rtx y)
+{
+  enum machine_mode submode = GET_MODE_INNER (mode);
+  bool imag_first;
+
+#ifdef PUSH_ROUNDING
+  unsigned int submodesize = GET_MODE_SIZE (submode);
+
+  /* In case we output to the stack, but the size is smaller than the
+     machine can push exactly, we need to use move instructions.  */
+  if (PUSH_ROUNDING (submodesize) != submodesize)
     {
-      enum insn_code insn_code;
-      enum machine_mode tmode = VOIDmode;
-      rtx x1 = x, y1 = y;
+      x = emit_move_resolve_push (mode, x);
+      return emit_move_insn (x, y);
+    }
+#endif
 
-      if (mode != CCmode
-         && mov_optab->handlers[(int) CCmode].insn_code != CODE_FOR_nothing)
-       tmode = CCmode;
-      else
-       for (tmode = QImode; tmode != VOIDmode;
-            tmode = GET_MODE_WIDER_MODE (tmode))
-         if (GET_MODE_SIZE (tmode) == GET_MODE_SIZE (mode))
-           break;
+  /* Note that the real part always precedes the imag part in memory
+     regardless of machine's endianness.  */
+  switch (GET_CODE (XEXP (x, 0)))
+    {
+    case PRE_DEC:
+    case POST_DEC:
+      imag_first = true;
+      break;
+    case PRE_INC:
+    case POST_INC:
+      imag_first = false;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
+                 read_complex_part (y, imag_first));
+  return emit_move_insn (gen_rtx_MEM (submode, XEXP (x, 0)),
+                        read_complex_part (y, !imag_first));
+}
 
-      gcc_assert (tmode != VOIDmode);
+/* A subroutine of emit_move_insn_1.  Generate a move from Y into X.
+   MODE is known to be complex.  Returns the last instruction emitted.  */
 
-      /* Get X and Y in TMODE.  We can't use gen_lowpart here because it
-        may call change_address which is not appropriate if we were
-        called when a reload was in progress.  We don't have to worry
-        about changing the address since the size in bytes is supposed to
-        be the same.  Copy the MEM to change the mode and move any
-        substitutions from the old MEM to the new one.  */
+static rtx
+emit_move_complex (enum machine_mode mode, rtx x, rtx y)
+{
+  bool try_int;
+
+  /* Need to take special care for pushes, to maintain proper ordering
+     of the data, and possibly extra padding.  */
+  if (push_operand (x, mode))
+    return emit_move_complex_push (mode, x, y);
+
+  /* See if we can coerce the target into moving both values at once.  */
+
+  /* Move floating point as parts.  */
+  if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
+      && mov_optab->handlers[GET_MODE_INNER (mode)].insn_code != CODE_FOR_nothing)
+    try_int = false;
+  /* Not possible if the values are inherently not adjacent.  */
+  else if (GET_CODE (x) == CONCAT || GET_CODE (y) == CONCAT)
+    try_int = false;
+  /* Is possible if both are registers (or subregs of registers).  */
+  else if (register_operand (x, mode) && register_operand (y, mode))
+    try_int = true;
+  /* If one of the operands is a memory, and alignment constraints
+     are friendly enough, we may be able to do combined memory operations.
+     We do not attempt this if Y is a constant because that combination is
+     usually better with the by-parts thing below.  */
+  else if ((MEM_P (x) ? !CONSTANT_P (y) : MEM_P (y))
+          && (!STRICT_ALIGNMENT
+              || get_mode_alignment (mode) == BIGGEST_ALIGNMENT))
+    try_int = true;
+  else
+    try_int = false;
+
+  if (try_int)
+    {
+      rtx ret;
 
-      if (reload_in_progress)
+      /* For memory to memory moves, optimal behavior can be had with the
+        existing block move logic.  */
+      if (MEM_P (x) && MEM_P (y))
        {
-         x = gen_lowpart_common (tmode, x1);
-         if (x == 0 && MEM_P (x1))
-           {
-             x = adjust_address_nv (x1, tmode, 0);
-             copy_replacements (x1, x);
-           }
+         emit_block_move (x, y, GEN_INT (GET_MODE_SIZE (mode)),
+                          BLOCK_OP_NO_LIBCALL);
+         return get_last_insn ();
+       }
 
-         y = gen_lowpart_common (tmode, y1);
-         if (y == 0 && MEM_P (y1))
-           {
-             y = adjust_address_nv (y1, tmode, 0);
-             copy_replacements (y1, y);
-           }
+      ret = emit_move_via_integer (mode, x, y);
+      if (ret)
+       return ret;
+    }
+
+  /* Show the output dies here.  This is necessary for SUBREGs
+     of pseudos since we cannot track their lifetimes correctly;
+     hard regs shouldn't appear here except as return values.  */
+  if (!reload_completed && !reload_in_progress
+      && REG_P (x) && !reg_overlap_mentioned_p (x, y))
+    emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
+
+  write_complex_part (x, read_complex_part (y, false), false);
+  write_complex_part (x, read_complex_part (y, true), true);
+  return get_last_insn ();
+}
+
+/* A subroutine of emit_move_insn_1.  Generate a move from Y into X.
+   MODE is known to be MODE_CC.  Returns the last instruction emitted.  */
+
+static rtx
+emit_move_ccmode (enum machine_mode mode, rtx x, rtx y)
+{
+  rtx ret;
+
+  /* Assume all MODE_CC modes are equivalent; if we have movcc, use it.  */
+  if (mode != CCmode)
+    {
+      enum insn_code code = mov_optab->handlers[CCmode].insn_code;
+      if (code != CODE_FOR_nothing)
+       {
+         x = emit_move_change_mode (CCmode, mode, x, true);
+         y = emit_move_change_mode (CCmode, mode, y, true);
+         return emit_insn (GEN_FCN (code) (x, y));
        }
-      else
+    }
+
+  /* Otherwise, find the MODE_INT mode of the same width.  */
+  ret = emit_move_via_integer (mode, x, y);
+  gcc_assert (ret != NULL);
+  return ret;
+}
+
+/* A subroutine of emit_move_insn_1.  Generate a move from Y into X.
+   MODE is any multi-word or full-word mode that lacks a move_insn
+   pattern.  Note that you will get better code if you define such
+   patterns, even if they must turn into multiple assembler instructions.  */
+
+static rtx
+emit_move_multi_word (enum machine_mode mode, rtx x, rtx y)
+{
+  rtx last_insn = 0;
+  rtx seq, inner;
+  bool need_clobber;
+  int i;
+      
+  gcc_assert (GET_MODE_SIZE (mode) >= UNITS_PER_WORD);
+      
+  /* If X is a push on the stack, do the push now and replace
+     X with a reference to the stack pointer.  */
+  if (push_operand (x, mode))
+    x = emit_move_resolve_push (mode, x);
+
+  /* If we are in reload, see if either operand is a MEM whose address
+     is scheduled for replacement.  */
+  if (reload_in_progress && MEM_P (x)
+      && (inner = find_replacement (&XEXP (x, 0))) != XEXP (x, 0))
+    x = replace_equiv_address_nv (x, inner);
+  if (reload_in_progress && MEM_P (y)
+      && (inner = find_replacement (&XEXP (y, 0))) != XEXP (y, 0))
+    y = replace_equiv_address_nv (y, inner);
+
+  start_sequence ();
+
+  need_clobber = false;
+  for (i = 0;
+       i < (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
+       i++)
+    {
+      rtx xpart = operand_subword (x, i, 1, mode);
+      rtx ypart = operand_subword (y, i, 1, mode);
+
+      /* If we can't get a part of Y, put Y into memory if it is a
+        constant.  Otherwise, force it into a register.  Then we must
+        be able to get a part of Y.  */
+      if (ypart == 0 && CONSTANT_P (y))
        {
-         x = gen_lowpart (tmode, x);
-         y = gen_lowpart (tmode, y);
+         y = force_const_mem (mode, y);
+         ypart = operand_subword (y, i, 1, mode);
        }
+      else if (ypart == 0)
+       ypart = operand_subword_force (y, i, mode);
+
+      gcc_assert (xpart && ypart);
 
-      insn_code = mov_optab->handlers[(int) tmode].insn_code;
-      return emit_insn (GEN_FCN (insn_code) (x, y));
+      need_clobber |= (GET_CODE (xpart) == SUBREG);
+
+      last_insn = emit_move_insn (xpart, ypart);
     }
 
+  seq = get_insns ();
+  end_sequence ();
+
+  /* Show the output dies here.  This is necessary for SUBREGs
+     of pseudos since we cannot track their lifetimes correctly;
+     hard regs shouldn't appear here except as return values.
+     We never want to emit such a clobber after reload.  */
+  if (x != y
+      && ! (reload_in_progress || reload_completed)
+      && need_clobber != 0)
+    emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
+
+  emit_insn (seq);
+
+  return last_insn;
+}
+
+/* Low level part of emit_move_insn.
+   Called just like emit_move_insn, but assumes X and Y
+   are basically valid.  */
+
+rtx
+emit_move_insn_1 (rtx x, rtx y)
+{
+  enum machine_mode mode = GET_MODE (x);
+  enum insn_code code;
+
+  gcc_assert ((unsigned int) mode < (unsigned int) MAX_MACHINE_MODE);
+
+  code = mov_optab->handlers[mode].insn_code;
+  if (code != CODE_FOR_nothing)
+    return emit_insn (GEN_FCN (code) (x, y));
+
+  /* Expand complex moves by moving real part and imag part.  */
+  if (COMPLEX_MODE_P (mode))
+    return emit_move_complex (mode, x, y);
+
+  if (GET_MODE_CLASS (mode) == MODE_CC)
+    return emit_move_ccmode (mode, x, y);
+
   /* Try using a move pattern for the corresponding integer mode.  This is
      only safe when simplify_subreg can convert MODE constants into integer
      constants.  At present, it can only do this reliably if the value
      fits within a HOST_WIDE_INT.  */
-  else if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
-          && (submode = int_mode_for_mode (mode)) != BLKmode
-          && mov_optab->handlers[submode].insn_code != CODE_FOR_nothing)
-    return emit_insn (GEN_FCN (mov_optab->handlers[submode].insn_code)
-                     (simplify_gen_subreg (submode, x, mode, 0),
-                      simplify_gen_subreg (submode, y, mode, 0)));
-
-  /* This will handle any multi-word or full-word mode that lacks a move_insn
-     pattern.  However, you will get better code if you define such patterns,
-     even if they must turn into multiple assembler instructions.  */
-  else
+  if (!CONSTANT_P (y) || GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
     {
-      rtx last_insn = 0;
-      rtx seq, inner;
-      int need_clobber;
-      int i;
-      
-      gcc_assert (GET_MODE_SIZE (mode) >= UNITS_PER_WORD);
-      
-#ifdef PUSH_ROUNDING
-
-      /* If X is a push on the stack, do the push now and replace
-        X with a reference to the stack pointer.  */
-      if (push_operand (x, GET_MODE (x)))
-       {
-         rtx temp;
-         enum rtx_code code;
+      rtx ret = emit_move_via_integer (mode, x, y);
+      if (ret)
+       return ret;
+    }
 
-         /* Do not use anti_adjust_stack, since we don't want to update
-            stack_pointer_delta.  */
-         temp = expand_binop (Pmode,
-#ifdef STACK_GROWS_DOWNWARD
-                              sub_optab,
-#else
-                              add_optab,
-#endif
-                              stack_pointer_rtx,
-                              GEN_INT
-                                (PUSH_ROUNDING
-                                 (GET_MODE_SIZE (GET_MODE (x)))),
-                              stack_pointer_rtx, 0, OPTAB_LIB_WIDEN);
-
-         if (temp != stack_pointer_rtx)
-           emit_move_insn (stack_pointer_rtx, temp);
-
-         code = GET_CODE (XEXP (x, 0));
-
-         /* Just hope that small offsets off SP are OK.  */
-         if (code == POST_INC)
-           temp = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
-                               GEN_INT (-((HOST_WIDE_INT)
-                                          GET_MODE_SIZE (GET_MODE (x)))));
-         else if (code == POST_DEC)
-           temp = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
-                               GEN_INT (GET_MODE_SIZE (GET_MODE (x))));
-         else
-           temp = stack_pointer_rtx;
+  return emit_move_multi_word (mode, x, y);
+}
 
-         x = change_address (x, VOIDmode, temp);
-       }
-#endif
+/* Generate code to copy Y into X.
+   Both Y and X must have the same mode, except that
+   Y can be a constant with VOIDmode.
+   This mode cannot be BLKmode; use emit_block_move for that.
 
-      /* If we are in reload, see if either operand is a MEM whose address
-        is scheduled for replacement.  */
-      if (reload_in_progress && MEM_P (x)
-         && (inner = find_replacement (&XEXP (x, 0))) != XEXP (x, 0))
-       x = replace_equiv_address_nv (x, inner);
-      if (reload_in_progress && MEM_P (y)
-         && (inner = find_replacement (&XEXP (y, 0))) != XEXP (y, 0))
-       y = replace_equiv_address_nv (y, inner);
+   Return the last instruction emitted.  */
 
-      start_sequence ();
+rtx
+emit_move_insn (rtx x, rtx y)
+{
+  enum machine_mode mode = GET_MODE (x);
+  rtx y_cst = NULL_RTX;
+  rtx last_insn, set;
 
-      need_clobber = 0;
-      for (i = 0;
-          i < (GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
-          i++)
-       {
-         rtx xpart = operand_subword (x, i, 1, mode);
-         rtx ypart = operand_subword (y, i, 1, mode);
+  gcc_assert (mode != BLKmode
+             && (GET_MODE (y) == mode || GET_MODE (y) == VOIDmode));
 
-         /* If we can't get a part of Y, put Y into memory if it is a
-            constant.  Otherwise, force it into a register.  If we still
-            can't get a part of Y, abort.  */
-         if (ypart == 0 && CONSTANT_P (y))
-           {
-             y = force_const_mem (mode, y);
-             ypart = operand_subword (y, i, 1, mode);
-           }
-         else if (ypart == 0)
-           ypart = operand_subword_force (y, i, mode);
+  if (CONSTANT_P (y))
+    {
+      if (optimize
+         && SCALAR_FLOAT_MODE_P (GET_MODE (x))
+         && (last_insn = compress_float_constant (x, y)))
+       return last_insn;
 
-         gcc_assert (xpart && ypart);
+      y_cst = y;
 
-         need_clobber |= (GET_CODE (xpart) == SUBREG);
+      if (!LEGITIMATE_CONSTANT_P (y))
+       {
+         y = force_const_mem (mode, y);
 
-         last_insn = emit_move_insn (xpart, ypart);
+         /* If the target's cannot_force_const_mem prevented the spill,
+            assume that the target's move expanders will also take care
+            of the non-legitimate constant.  */
+         if (!y)
+           y = y_cst;
        }
+    }
 
-      seq = get_insns ();
-      end_sequence ();
+  /* If X or Y are memory references, verify that their addresses are valid
+     for the machine.  */
+  if (MEM_P (x)
+      && ((! memory_address_p (GET_MODE (x), XEXP (x, 0))
+          && ! push_operand (x, GET_MODE (x)))
+         || (flag_force_addr
+             && CONSTANT_ADDRESS_P (XEXP (x, 0)))))
+    x = validize_mem (x);
 
-      /* Show the output dies here.  This is necessary for SUBREGs
-        of pseudos since we cannot track their lifetimes correctly;
-        hard regs shouldn't appear here except as return values.
-        We never want to emit such a clobber after reload.  */
-      if (x != y
-         && ! (reload_in_progress || reload_completed)
-         && need_clobber != 0)
-       emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
+  if (MEM_P (y)
+      && (! memory_address_p (GET_MODE (y), XEXP (y, 0))
+         || (flag_force_addr
+             && CONSTANT_ADDRESS_P (XEXP (y, 0)))))
+    y = validize_mem (y);
 
-      emit_insn (seq);
+  gcc_assert (mode != BLKmode);
 
-      return last_insn;
-    }
+  last_insn = emit_move_insn_1 (x, y);
+
+  if (y_cst && REG_P (x)
+      && (set = single_set (last_insn)) != NULL_RTX
+      && SET_DEST (set) == x
+      && ! rtx_equal_p (y_cst, SET_SRC (set)))
+    set_unique_reg_note (last_insn, REG_EQUAL, y_cst);
+
+  return last_insn;
 }
 
 /* If Y is representable exactly in a narrower mode, and the target can
@@ -2891,9 +3204,15 @@ compress_float_constant (rtx x, rtx y)
   enum machine_mode orig_srcmode = GET_MODE (y);
   enum machine_mode srcmode;
   REAL_VALUE_TYPE r;
+  int oldcost, newcost;
 
   REAL_VALUE_FROM_CONST_DOUBLE (r, y);
 
+  if (LEGITIMATE_CONSTANT_P (y))
+    oldcost = rtx_cost (y, SET);
+  else
+    oldcost = rtx_cost (force_const_mem (dstmode, y), SET);
+
   for (srcmode = GET_CLASS_NARROWEST_MODE (GET_MODE_CLASS (orig_srcmode));
        srcmode != orig_srcmode;
        srcmode = GET_MODE_WIDER_MODE (srcmode))
@@ -2918,12 +3237,23 @@ compress_float_constant (rtx x, rtx y)
             the extension.  */
          if (! (*insn_data[ic].operand[1].predicate) (trunc_y, srcmode))
            continue;
+         /* This is valid, but may not be cheaper than the original. */
+         newcost = rtx_cost (gen_rtx_FLOAT_EXTEND (dstmode, trunc_y), SET);
+         if (oldcost < newcost)
+           continue;
        }
       else if (float_extend_from_mem[dstmode][srcmode])
-       trunc_y = validize_mem (force_const_mem (srcmode, trunc_y));
+       {
+         trunc_y = force_const_mem (srcmode, trunc_y);
+         /* This is valid, but may not be cheaper than the original. */
+         newcost = rtx_cost (gen_rtx_FLOAT_EXTEND (dstmode, trunc_y), SET);
+         if (oldcost < newcost)
+           continue;
+         trunc_y = validize_mem (trunc_y);
+       }
       else
        continue;
-
       emit_unop_insn (ic, x, trunc_y, UNKNOWN);
       last_insn = get_last_insn ();
 
@@ -3093,9 +3423,8 @@ emit_single_push_insn (enum machine_mode mode, rtx x, tree type)
    ALIGN (in bits) is maximum alignment we can assume.
 
    If PARTIAL and REG are both nonzero, then copy that many of the first
-   words of X into registers starting with REG, and push the rest of X.
-   The amount of space pushed is decreased by PARTIAL words,
-   rounded *down* to a multiple of PARM_BOUNDARY.
+   bytes of X into registers starting with REG, and push the rest of X.
+   The amount of space pushed is decreased by PARTIAL bytes.
    REG must be a hard register in this case.
    If REG is zero but PARTIAL is not, take any all others actions for an
    argument partially in registers, but do not actually load any
@@ -3147,24 +3476,15 @@ emit_push_insn (rtx x, enum machine_mode mode, tree type, rtx size,
       /* Copy a block into the stack, entirely or partially.  */
 
       rtx temp;
-      int used = partial * UNITS_PER_WORD;
+      int used;
       int offset;
       int skip;
 
-      if (reg && GET_CODE (reg) == PARALLEL)
-       {
-         /* Use the size of the elt to compute offset.  */
-         rtx elt = XEXP (XVECEXP (reg, 0, 0), 0);
-         used = partial * GET_MODE_SIZE (GET_MODE (elt));
-         offset = used % (PARM_BOUNDARY / BITS_PER_UNIT);
-       }
-      else
-       offset = used % (PARM_BOUNDARY / BITS_PER_UNIT);
+      offset = partial % (PARM_BOUNDARY / BITS_PER_UNIT);
+      used = partial - offset;
 
       gcc_assert (size);
 
-      used -= offset;
-
       /* USED is now the # of bytes we need not copy to the stack
         because registers will take care of them.  */
 
@@ -3275,9 +3595,9 @@ emit_push_insn (rtx x, enum machine_mode mode, tree type, rtx size,
       int size = GET_MODE_SIZE (mode) / UNITS_PER_WORD;
       int i;
       int not_stack;
-      /* # words of start of argument
+      /* # bytes of start of argument
         that we must make space for but need not store.  */
-      int offset = partial % (PARM_BOUNDARY / BITS_PER_WORD);
+      int offset = partial % (PARM_BOUNDARY / BITS_PER_UNIT);
       int args_offset = INTVAL (args_so_far);
       int skip;
 
@@ -3295,8 +3615,9 @@ emit_push_insn (rtx x, enum machine_mode mode, tree type, rtx size,
        offset = 0;
 
       /* Now NOT_STACK gets the number of words that we don't need to
-        allocate on the stack.  */
-      not_stack = partial - offset;
+        allocate on the stack. Convert OFFSET to words too. */
+      not_stack = (partial - offset) / UNITS_PER_WORD;
+      offset /= UNITS_PER_WORD;
 
       /* If the partial register-part of the arg counts in its stack size,
         skip the part of stack space corresponding to the registers.
@@ -3380,7 +3701,10 @@ emit_push_insn (rtx x, enum machine_mode mode, tree type, rtx size,
       if (GET_CODE (reg) == PARALLEL)
        emit_group_load (reg, x, type, -1);
       else
-       move_block_to_reg (REGNO (reg), x, partial, mode);
+       {
+         gcc_assert (partial % UNITS_PER_WORD == 0);
+         move_block_to_reg (REGNO (reg), x, partial / UNITS_PER_WORD, mode);
+       }
     }
 
   if (extra && args_addr == 0 && where_pad == stack_direction)
@@ -3405,13 +3729,163 @@ get_subtarget (rtx x)
          ? 0 : x);
 }
 
-/* Expand an assignment that stores the value of FROM into TO.
-   If WANT_VALUE is nonzero, return an rtx for the value of TO.
-   (If the value is constant, this rtx is a constant.)
-   Otherwise, the returned value is NULL_RTX.  */
+/* A subroutine of expand_assignment.  Optimize FIELD op= VAL, where
+   FIELD is a bitfield.  Returns true if the optimization was successful,
+   and there's nothing else to do.  */
 
-rtx
-expand_assignment (tree to, tree from, int want_value)
+static bool
+optimize_bitfield_assignment_op (unsigned HOST_WIDE_INT bitsize,
+                                unsigned HOST_WIDE_INT bitpos,
+                                enum machine_mode mode1, rtx str_rtx,
+                                tree to, tree src)
+{
+  enum machine_mode str_mode = GET_MODE (str_rtx);
+  unsigned int str_bitsize = GET_MODE_BITSIZE (str_mode);
+  tree op0, op1;
+  rtx value, result;
+  optab binop;
+
+  if (mode1 != VOIDmode
+      || bitsize >= BITS_PER_WORD
+      || str_bitsize > BITS_PER_WORD
+      || TREE_SIDE_EFFECTS (to)
+      || TREE_THIS_VOLATILE (to))
+    return false;
+
+  STRIP_NOPS (src);
+  if (!BINARY_CLASS_P (src)
+      || TREE_CODE (TREE_TYPE (src)) != INTEGER_TYPE)
+    return false;
+
+  op0 = TREE_OPERAND (src, 0);
+  op1 = TREE_OPERAND (src, 1);
+  STRIP_NOPS (op0);
+
+  if (!operand_equal_p (to, op0, 0))
+    return false;
+
+  if (MEM_P (str_rtx))
+    {
+      unsigned HOST_WIDE_INT offset1;
+
+      if (str_bitsize == 0 || str_bitsize > BITS_PER_WORD)
+       str_mode = word_mode;
+      str_mode = get_best_mode (bitsize, bitpos,
+                               MEM_ALIGN (str_rtx), str_mode, 0);
+      if (str_mode == VOIDmode)
+       return false;
+      str_bitsize = GET_MODE_BITSIZE (str_mode);
+
+      offset1 = bitpos;
+      bitpos %= str_bitsize;
+      offset1 = (offset1 - bitpos) / BITS_PER_UNIT;
+      str_rtx = adjust_address (str_rtx, str_mode, offset1);
+    }
+  else if (!REG_P (str_rtx) && GET_CODE (str_rtx) != SUBREG)
+    return false;
+
+  /* If the bit field covers the whole REG/MEM, store_field
+     will likely generate better code.  */
+  if (bitsize >= str_bitsize)
+    return false;
+
+  /* We can't handle fields split across multiple entities.  */
+  if (bitpos + bitsize > str_bitsize)
+    return false;
+
+  if (BYTES_BIG_ENDIAN)
+    bitpos = str_bitsize - bitpos - bitsize;
+
+  switch (TREE_CODE (src))
+    {
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+      /* For now, just optimize the case of the topmost bitfield
+        where we don't need to do any masking and also
+        1 bit bitfields where xor can be used.
+        We might win by one instruction for the other bitfields
+        too if insv/extv instructions aren't used, so that
+        can be added later.  */
+      if (bitpos + bitsize != str_bitsize
+         && (bitsize != 1 || TREE_CODE (op1) != INTEGER_CST))
+       break;
+
+      value = expand_expr (op1, NULL_RTX, str_mode, 0);
+      value = convert_modes (str_mode,
+                            TYPE_MODE (TREE_TYPE (op1)), value,
+                            TYPE_UNSIGNED (TREE_TYPE (op1)));
+
+      /* We may be accessing data outside the field, which means
+        we can alias adjacent data.  */
+      if (MEM_P (str_rtx))
+       {
+         str_rtx = shallow_copy_rtx (str_rtx);
+         set_mem_alias_set (str_rtx, 0);
+         set_mem_expr (str_rtx, 0);
+       }
+
+      binop = TREE_CODE (src) == PLUS_EXPR ? add_optab : sub_optab;
+      if (bitsize == 1 && bitpos + bitsize != str_bitsize)
+       {
+         value = expand_and (str_mode, value, const1_rtx, NULL);
+         binop = xor_optab;
+       }
+      value = expand_shift (LSHIFT_EXPR, str_mode, value,
+                           build_int_cst (NULL_TREE, bitpos),
+                           NULL_RTX, 1);
+      result = expand_binop (str_mode, binop, str_rtx,
+                            value, str_rtx, 1, OPTAB_WIDEN);
+      if (result != str_rtx)
+       emit_move_insn (str_rtx, result);
+      return true;
+
+    case BIT_IOR_EXPR:
+    case BIT_XOR_EXPR:
+      if (TREE_CODE (op1) != INTEGER_CST)
+       break;
+      value = expand_expr (op1, NULL_RTX, GET_MODE (str_rtx), 0);
+      value = convert_modes (GET_MODE (str_rtx),
+                            TYPE_MODE (TREE_TYPE (op1)), value,
+                            TYPE_UNSIGNED (TREE_TYPE (op1)));
+
+      /* We may be accessing data outside the field, which means
+        we can alias adjacent data.  */
+      if (MEM_P (str_rtx))
+       {
+         str_rtx = shallow_copy_rtx (str_rtx);
+         set_mem_alias_set (str_rtx, 0);
+         set_mem_expr (str_rtx, 0);
+       }
+
+      binop = TREE_CODE (src) == BIT_IOR_EXPR ? ior_optab : xor_optab;
+      if (bitpos + bitsize != GET_MODE_BITSIZE (GET_MODE (str_rtx)))
+       {
+         rtx mask = GEN_INT (((unsigned HOST_WIDE_INT) 1 << bitsize)
+                             - 1);
+         value = expand_and (GET_MODE (str_rtx), value, mask,
+                             NULL_RTX);
+       }
+      value = expand_shift (LSHIFT_EXPR, GET_MODE (str_rtx), value,
+                           build_int_cst (NULL_TREE, bitpos),
+                           NULL_RTX, 1);
+      result = expand_binop (GET_MODE (str_rtx), binop, str_rtx,
+                            value, str_rtx, 1, OPTAB_WIDEN);
+      if (result != str_rtx)
+       emit_move_insn (str_rtx, result);
+      return true;
+
+    default:
+      break;
+    }
+
+  return false;
+}
+
+
+/* Expand an assignment that stores the value of FROM into TO.  */
+
+void
+expand_assignment (tree to, tree from)
 {
   rtx to_rtx = 0;
   rtx result;
@@ -3421,7 +3895,7 @@ expand_assignment (tree to, tree from, int want_value)
   if (TREE_CODE (to) == ERROR_MARK)
     {
       result = expand_expr (from, NULL_RTX, VOIDmode, 0);
-      return want_value ? result : NULL_RTX;
+      return;
     }
 
   /* Assignment of a structure component needs special treatment
@@ -3429,14 +3903,11 @@ expand_assignment (tree to, tree from, int want_value)
      Assignment of an array element at a constant index, and assignment of
      an array element in an unaligned packed structure field, has the same
      problem.  */
-
-  if (TREE_CODE (to) == COMPONENT_REF || TREE_CODE (to) == BIT_FIELD_REF
-      || TREE_CODE (to) == ARRAY_REF || TREE_CODE (to) == ARRAY_RANGE_REF
+  if (handled_component_p (to)
       || TREE_CODE (TREE_TYPE (to)) == ARRAY_TYPE)
     {
       enum machine_mode mode1;
       HOST_WIDE_INT bitsize, bitpos;
-      rtx orig_to_rtx;
       tree offset;
       int unsignedp;
       int volatilep = 0;
@@ -3444,15 +3915,12 @@ expand_assignment (tree to, tree from, int want_value)
 
       push_temp_slots ();
       tem = get_inner_reference (to, &bitsize, &bitpos, &offset, &mode1,
-                                &unsignedp, &volatilep);
+                                &unsignedp, &volatilep, true);
 
       /* If we are going to use store_bit_field and extract_bit_field,
         make sure to_rtx will be safe for multiple use.  */
 
-      if (mode1 == VOIDmode && want_value)
-       tem = stabilize_reference (tem);
-
-      orig_to_rtx = to_rtx = expand_expr (tem, NULL_RTX, VOIDmode, 0);
+      to_rtx = expand_expr (tem, NULL_RTX, VOIDmode, 0);
 
       if (offset != 0)
        {
@@ -3487,163 +3955,51 @@ expand_assignment (tree to, tree from, int want_value)
                                                                   offset));
        }
 
-      if (MEM_P (to_rtx))
+      /* Handle expand_expr of a complex value returning a CONCAT.  */
+      if (GET_CODE (to_rtx) == CONCAT)
        {
-         /* If the field is at offset zero, we could have been given the
-            DECL_RTX of the parent struct.  Don't munge it.  */
-         to_rtx = shallow_copy_rtx (to_rtx);
-
-         set_mem_attributes_minus_bitpos (to_rtx, to, 0, bitpos);
-       }
-
-      /* Deal with volatile and readonly fields.  The former is only done
-        for MEM.  Also set MEM_KEEP_ALIAS_SET_P if needed.  */
-      if (volatilep && MEM_P (to_rtx))
-       {
-         if (to_rtx == orig_to_rtx)
-           to_rtx = copy_rtx (to_rtx);
-         MEM_VOLATILE_P (to_rtx) = 1;
-       }
-
-      if (MEM_P (to_rtx) && ! can_address_p (to))
-       {
-         if (to_rtx == orig_to_rtx)
-           to_rtx = copy_rtx (to_rtx);
-         MEM_KEEP_ALIAS_SET_P (to_rtx) = 1;
+         if (TREE_CODE (TREE_TYPE (from)) == COMPLEX_TYPE)
+           {
+             gcc_assert (bitpos == 0);
+             result = store_expr (from, to_rtx, false);
+           }
+         else
+           {
+             gcc_assert (bitpos == 0 || bitpos == GET_MODE_BITSIZE (mode1));
+             result = store_expr (from, XEXP (to_rtx, bitpos != 0), false);
+           }
        }
-
-      /* Optimize bitfld op= val in certain cases.  */
-      while (mode1 == VOIDmode && !want_value
-            && bitsize > 0 && bitsize < BITS_PER_WORD
-            && GET_MODE_BITSIZE (GET_MODE (to_rtx)) <= BITS_PER_WORD
-            && !TREE_SIDE_EFFECTS (to)
-            && !TREE_THIS_VOLATILE (to))
+      else
        {
-         tree src, op0, op1;
-         rtx value, str_rtx = to_rtx;
-         HOST_WIDE_INT bitpos1 = bitpos;
-         optab binop;
-
-         src = from;
-         STRIP_NOPS (src);
-         if (TREE_CODE (TREE_TYPE (src)) != INTEGER_TYPE
-             || TREE_CODE_CLASS (TREE_CODE (src)) != '2')
-           break;
-
-         op0 = TREE_OPERAND (src, 0);
-         op1 = TREE_OPERAND (src, 1);
-         STRIP_NOPS (op0);
-
-         if (! operand_equal_p (to, op0, 0))
-           break;
-
-         if (MEM_P (str_rtx))
+         if (MEM_P (to_rtx))
            {
-             enum machine_mode mode = GET_MODE (str_rtx);
-             HOST_WIDE_INT offset1;
+             /* If the field is at offset zero, we could have been given the
+                DECL_RTX of the parent struct.  Don't munge it.  */
+             to_rtx = shallow_copy_rtx (to_rtx);
 
-             if (GET_MODE_BITSIZE (mode) == 0
-                 || GET_MODE_BITSIZE (mode) > BITS_PER_WORD)
-               mode = word_mode;
-             mode = get_best_mode (bitsize, bitpos1, MEM_ALIGN (str_rtx),
-                                   mode, 0);
-             if (mode == VOIDmode)
-               break;
-
-             offset1 = bitpos1;
-             bitpos1 %= GET_MODE_BITSIZE (mode);
-             offset1 = (offset1 - bitpos1) / BITS_PER_UNIT;
-             str_rtx = adjust_address (str_rtx, mode, offset1);
-           }
-         else if (!REG_P (str_rtx) && GET_CODE (str_rtx) != SUBREG)
-           break;
-
-         /* If the bit field covers the whole REG/MEM, store_field
-            will likely generate better code.  */
-         if (bitsize >= GET_MODE_BITSIZE (GET_MODE (str_rtx)))
-           break;
-
-         /* We can't handle fields split across multiple entities.  */
-         if (bitpos1 + bitsize > GET_MODE_BITSIZE (GET_MODE (str_rtx)))
-           break;
-
-         if (BYTES_BIG_ENDIAN)
-           bitpos1 = GET_MODE_BITSIZE (GET_MODE (str_rtx)) - bitpos1
-                     - bitsize;
-
-         /* Special case some bitfield op= exp.  */
-         switch (TREE_CODE (src))
-           {
-           case PLUS_EXPR:
-           case MINUS_EXPR:
-             /* For now, just optimize the case of the topmost bitfield
-                where we don't need to do any masking and also
-                1 bit bitfields where xor can be used.
-                We might win by one instruction for the other bitfields
-                too if insv/extv instructions aren't used, so that
-                can be added later.  */
-             if (bitpos1 + bitsize != GET_MODE_BITSIZE (GET_MODE (str_rtx))
-                 && (bitsize != 1 || TREE_CODE (op1) != INTEGER_CST))
-               break;
-             value = expand_expr (op1, NULL_RTX, GET_MODE (str_rtx), 0);
-             value = convert_modes (GET_MODE (str_rtx),
-                                    TYPE_MODE (TREE_TYPE (op1)), value,
-                                    TYPE_UNSIGNED (TREE_TYPE (op1)));
-
-             /* We may be accessing data outside the field, which means
-                we can alias adjacent data.  */
-             if (MEM_P (str_rtx))
-               {
-                 str_rtx = shallow_copy_rtx (str_rtx);
-                 set_mem_alias_set (str_rtx, 0);
-                 set_mem_expr (str_rtx, 0);
-               }
+             set_mem_attributes_minus_bitpos (to_rtx, to, 0, bitpos);
 
-             binop = TREE_CODE (src) == PLUS_EXPR ? add_optab : sub_optab;
-             if (bitsize == 1
-                 && bitpos1 + bitsize != GET_MODE_BITSIZE (GET_MODE (str_rtx)))
-               {
-                 value = expand_and (GET_MODE (str_rtx), value, const1_rtx,
-                                     NULL_RTX);
-                 binop = xor_optab;
-               }
-             value = expand_shift (LSHIFT_EXPR, GET_MODE (str_rtx), value,
-                                   build_int_cst (NULL_TREE, bitpos1),
-                                   NULL_RTX, 1);
-             result = expand_binop (GET_MODE (str_rtx), binop, str_rtx,
-                                    value, str_rtx, 1, OPTAB_WIDEN);
-             if (result != str_rtx)
-               emit_move_insn (str_rtx, result);
-             free_temp_slots ();
-             pop_temp_slots ();
-             return NULL_RTX;
-
-           default:
-             break;
+             /* Deal with volatile and readonly fields.  The former is only
+                done for MEM.  Also set MEM_KEEP_ALIAS_SET_P if needed.  */
+             if (volatilep)
+               MEM_VOLATILE_P (to_rtx) = 1;
+             if (component_uses_parent_alias_set (to))
+               MEM_KEEP_ALIAS_SET_P (to_rtx) = 1;
            }
 
-         break;
+         if (optimize_bitfield_assignment_op (bitsize, bitpos, mode1,
+                                              to_rtx, to, from))
+           result = NULL;
+         else
+           result = store_field (to_rtx, bitsize, bitpos, mode1, from,
+                                 TREE_TYPE (tem), get_alias_set (to));
        }
 
-      result = store_field (to_rtx, bitsize, bitpos, mode1, from,
-                           (want_value
-                            /* Spurious cast for HPUX compiler.  */
-                            ? ((enum machine_mode)
-                               TYPE_MODE (TREE_TYPE (to)))
-                            : VOIDmode),
-                           unsignedp, TREE_TYPE (tem), get_alias_set (to));
-
-      preserve_temp_slots (result);
+      if (result)
+       preserve_temp_slots (result);
       free_temp_slots ();
       pop_temp_slots ();
-
-      /* If the value is meaningful, convert RESULT to the proper mode.
-        Otherwise, return nothing.  */
-      return (want_value ? convert_modes (TYPE_MODE (TREE_TYPE (to)),
-                                         TYPE_MODE (TREE_TYPE (from)),
-                                         result,
-                                         TYPE_UNSIGNED (TREE_TYPE (to)))
-             : NULL_RTX);
+      return;
     }
 
   /* If the rhs is a function call and its value is not an aggregate,
@@ -3684,7 +4040,7 @@ expand_assignment (tree to, tree from, int want_value)
       preserve_temp_slots (to_rtx);
       free_temp_slots ();
       pop_temp_slots ();
-      return want_value ? to_rtx : NULL_RTX;
+      return;
     }
 
   /* Ordinary treatment.  Expand TO to get a REG or MEM rtx.
@@ -3711,7 +4067,7 @@ expand_assignment (tree to, tree from, int want_value)
       preserve_temp_slots (to_rtx);
       free_temp_slots ();
       pop_temp_slots ();
-      return want_value ? to_rtx : NULL_RTX;
+      return;
     }
 
   /* In case we are returning the contents of an object which overlaps
@@ -3737,30 +4093,22 @@ expand_assignment (tree to, tree from, int want_value)
       preserve_temp_slots (to_rtx);
       free_temp_slots ();
       pop_temp_slots ();
-      return want_value ? to_rtx : NULL_RTX;
+      return;
     }
 
   /* Compute FROM and store the value in the rtx we got.  */
 
   push_temp_slots ();
-  result = store_expr (from, to_rtx, want_value);
+  result = store_expr (from, to_rtx, 0);
   preserve_temp_slots (result);
   free_temp_slots ();
   pop_temp_slots ();
-  return want_value ? result : NULL_RTX;
+  return;
 }
 
 /* Generate code for computing expression EXP,
    and storing the value into TARGET.
 
-   If WANT_VALUE & 1 is nonzero, return a copy of the value
-   not in TARGET, so that we can be sure to use the proper
-   value in a containing expression even if TARGET has something
-   else stored in it.  If possible, we copy the value through a pseudo
-   and return that pseudo.  Or, if the value is constant, we try to
-   return the constant.  In some cases, we return a pseudo
-   copied *from* TARGET.
-
    If the mode is BLKmode then we may return TARGET itself.
    It turns out that in BLKmode it doesn't cause a problem.
    because C has no operators that could combine two different
@@ -3768,27 +4116,22 @@ expand_assignment (tree to, tree from, int want_value)
    with no sequence point.  Will other languages need this to
    be more thorough?
 
-   If WANT_VALUE & 1 is 0, we return NULL, to make sure
-   to catch quickly any cases where the caller uses the value
-   and fails to set WANT_VALUE.
-
-   If WANT_VALUE & 2 is set, this is a store into a call param on the
+   If CALL_PARAM_P is nonzero, this is a store into a call param on the
    stack, and block moves may need to be treated specially.  */
 
 rtx
-store_expr (tree exp, rtx target, int want_value)
+store_expr (tree exp, rtx target, int call_param_p)
 {
   rtx temp;
   rtx alt_rtl = NULL_RTX;
   int dont_return_target = 0;
-  int dont_store_target = 0;
 
   if (VOID_TYPE_P (TREE_TYPE (exp)))
     {
       /* C++ can generate ?: expressions with a throw expression in one
         branch and an rvalue in the other. Here, we resolve attempts to
         store the throw expression's nonexistent result.  */
-      gcc_assert (!want_value);
+      gcc_assert (!call_param_p);
       expand_expr (exp, const0_rtx, VOIDmode, 0);
       return NULL_RTX;
     }
@@ -3797,8 +4140,8 @@ store_expr (tree exp, rtx target, int want_value)
       /* Perform first part of compound expression, then assign from second
         part.  */
       expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode,
-                  want_value & 2 ? EXPAND_STACK_PARM : EXPAND_NORMAL);
-      return store_expr (TREE_OPERAND (exp, 1), target, want_value);
+                  call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL);
+      return store_expr (TREE_OPERAND (exp, 1), target, call_param_p);
     }
   else if (TREE_CODE (exp) == COND_EXPR && GET_MODE (target) == BLKmode)
     {
@@ -3812,40 +4155,15 @@ store_expr (tree exp, rtx target, int want_value)
       do_pending_stack_adjust ();
       NO_DEFER_POP;
       jumpifnot (TREE_OPERAND (exp, 0), lab1);
-      store_expr (TREE_OPERAND (exp, 1), target, want_value & 2);
+      store_expr (TREE_OPERAND (exp, 1), target, call_param_p);
       emit_jump_insn (gen_jump (lab2));
       emit_barrier ();
       emit_label (lab1);
-      store_expr (TREE_OPERAND (exp, 2), target, want_value & 2);
+      store_expr (TREE_OPERAND (exp, 2), target, call_param_p);
       emit_label (lab2);
       OK_DEFER_POP;
 
-      return want_value & 1 ? target : NULL_RTX;
-    }
-  else if ((want_value & 1) != 0
-          && MEM_P (target)
-          && ! MEM_VOLATILE_P (target)
-          && GET_MODE (target) != BLKmode)
-    /* If target is in memory and caller wants value in a register instead,
-       arrange that.  Pass TARGET as target for expand_expr so that,
-       if EXP is another assignment, WANT_VALUE will be nonzero for it.
-       We know expand_expr will not use the target in that case.
-       Don't do this if TARGET is volatile because we are supposed
-       to write it and then read it.  */
-    {
-      temp = expand_expr (exp, target, GET_MODE (target),
-                         want_value & 2 ? EXPAND_STACK_PARM : EXPAND_NORMAL);
-      if (GET_MODE (temp) != BLKmode && GET_MODE (temp) != VOIDmode)
-       {
-         /* If TEMP is already in the desired TARGET, only copy it from
-            memory and don't store it there again.  */
-         if (temp == target
-             || (rtx_equal_p (temp, target)
-                 && ! side_effects_p (temp) && ! side_effects_p (target)))
-           dont_store_target = 1;
-         temp = copy_to_reg (temp);
-       }
-      dont_return_target = 1;
+      return NULL_RTX;
     }
   else if (GET_CODE (target) == SUBREG && SUBREG_PROMOTED_VAR_P (target))
     /* If this is a scalar in a register that is stored in a wider mode
@@ -3855,14 +4173,13 @@ store_expr (tree exp, rtx target, int want_value)
     {
       rtx inner_target = 0;
 
-      /* If we don't want a value, we can do the conversion inside EXP,
-        which will often result in some optimizations.  Do the conversion
-        in two steps: first change the signedness, if needed, then
-        the extend.  But don't do this if the type of EXP is a subtype
-        of something else since then the conversion might involve
-        more than just converting modes.  */
-      if ((want_value & 1) == 0
-         && INTEGRAL_TYPE_P (TREE_TYPE (exp))
+      /* We can do the conversion inside EXP, which will often result
+        in some optimizations.  Do the conversion in two steps: first
+        change the signedness, if needed, then the extend.  But don't
+        do this if the type of EXP is a subtype of something else
+        since then the conversion might involve more than just
+        converting modes.  */
+      if (INTEGRAL_TYPE_P (TREE_TYPE (exp))
          && TREE_TYPE (TREE_TYPE (exp)) == 0
          && (!lang_hooks.reduce_bit_field_operations
              || (GET_MODE_PRECISION (GET_MODE (target))
@@ -3883,15 +4200,7 @@ store_expr (tree exp, rtx target, int want_value)
        }
 
       temp = expand_expr (exp, inner_target, VOIDmode,
-                         want_value & 2 ? EXPAND_STACK_PARM : EXPAND_NORMAL);
-
-      /* If TEMP is a MEM and we want a result value, make the access
-        now so it gets done only once.  Strictly speaking, this is
-        only necessary if the MEM is volatile, or if the address
-        overlaps TARGET.  But not performing the load twice also
-        reduces the amount of rtl we generate and then have to CSE.  */
-      if (MEM_P (temp) && (want_value & 1) != 0)
-       temp = copy_to_reg (temp);
+                         call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL);
 
       /* If TEMP is a VOIDmode constant, use convert_modes to make
         sure that we properly convert it.  */
@@ -3907,31 +4216,12 @@ store_expr (tree exp, rtx target, int want_value)
       convert_move (SUBREG_REG (target), temp,
                    SUBREG_PROMOTED_UNSIGNED_P (target));
 
-      /* If we promoted a constant, change the mode back down to match
-        target.  Otherwise, the caller might get confused by a result whose
-        mode is larger than expected.  */
-
-      if ((want_value & 1) != 0 && GET_MODE (temp) != GET_MODE (target))
-       {
-         if (GET_MODE (temp) != VOIDmode)
-           {
-             temp = gen_lowpart_SUBREG (GET_MODE (target), temp);
-             SUBREG_PROMOTED_VAR_P (temp) = 1;
-             SUBREG_PROMOTED_UNSIGNED_SET (temp,
-               SUBREG_PROMOTED_UNSIGNED_P (target));
-           }
-         else
-           temp = convert_modes (GET_MODE (target),
-                                 GET_MODE (SUBREG_REG (target)),
-                                 temp, SUBREG_PROMOTED_UNSIGNED_P (target));
-       }
-
-      return want_value & 1 ? temp : NULL_RTX;
+      return NULL_RTX;
     }
   else
     {
       temp = expand_expr_real (exp, target, GET_MODE (target),
-                              (want_value & 2
+                              (call_param_p
                                ? EXPAND_STACK_PARM : EXPAND_NORMAL),
                               &alt_rtl);
       /* Return TARGET if it's a specified hardware register.
@@ -3945,7 +4235,7 @@ store_expr (tree exp, rtx target, int want_value)
            && REGNO (target) < FIRST_PSEUDO_REGISTER)
          && !(MEM_P (target) && MEM_VOLATILE_P (target))
          && ! rtx_equal_p (temp, target)
-         && (CONSTANT_P (temp) || (want_value & 1) != 0))
+         && CONSTANT_P (temp))
        dont_return_target = 1;
     }
 
@@ -3981,15 +4271,14 @@ store_expr (tree exp, rtx target, int want_value)
        || (temp != target && (side_effects_p (temp)
                              || side_effects_p (target))))
       && TREE_CODE (exp) != ERROR_MARK
-      && ! dont_store_target
       /* If store_expr stores a DECL whose DECL_RTL(exp) == TARGET,
         but TARGET is not valid memory reference, TEMP will differ
         from TARGET although it is really the same location.  */
       && !(alt_rtl && rtx_equal_p (alt_rtl, target))
-      /* If there's nothing to copy, don't bother.  Don't call expr_size
-        unless necessary, because some front-ends (C++) expr_size-hook
-        aborts on objects that are not supposed to be bit-copied or
-        bit-initialized.  */
+      /* If there's nothing to copy, don't bother.  Don't call
+        expr_size unless necessary, because some front-ends (C++)
+        expr_size-hook must not be given objects that are not
+        supposed to be bit-copied or bit-initialized.  */
       && expr_size (exp) != const0_rtx)
     {
       if (GET_MODE (temp) != GET_MODE (target)
@@ -4019,7 +4308,7 @@ store_expr (tree exp, rtx target, int want_value)
          if (GET_CODE (size) == CONST_INT
              && INTVAL (size) < TREE_STRING_LENGTH (exp))
            emit_block_move (target, temp, size,
-                            (want_value & 2
+                            (call_param_p
                              ? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
          else
            {
@@ -4030,7 +4319,7 @@ store_expr (tree exp, rtx target, int want_value)
                              size_int (TREE_STRING_LENGTH (exp)));
              rtx copy_size_rtx
                = expand_expr (copy_size, NULL_RTX, VOIDmode,
-                              (want_value & 2
+                              (call_param_p
                                ? EXPAND_STACK_PARM : EXPAND_NORMAL));
              rtx label = 0;
 
@@ -4038,7 +4327,7 @@ store_expr (tree exp, rtx target, int want_value)
              copy_size_rtx = convert_to_mode (ptr_mode, copy_size_rtx,
                                               TYPE_UNSIGNED (sizetype));
              emit_block_move (target, temp, copy_size_rtx,
-                              (want_value & 2
+                              (call_param_p
                                ? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
 
              /* Figure out how much is left in TARGET that we have to clear.
@@ -4069,7 +4358,7 @@ store_expr (tree exp, rtx target, int want_value)
                }
 
              if (size != const0_rtx)
-               clear_storage (target, size);
+               clear_storage (target, size, BLOCK_OP_NORMAL);
 
              if (label)
                emit_label (label);
@@ -4082,7 +4371,7 @@ store_expr (tree exp, rtx target, int want_value)
                         int_size_in_bytes (TREE_TYPE (exp)));
       else if (GET_MODE (temp) == BLKmode)
        emit_block_move (target, temp, expr_size (exp),
-                        (want_value & 2
+                        (call_param_p
                          ? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
       else
        {
@@ -4092,44 +4381,35 @@ store_expr (tree exp, rtx target, int want_value)
        }
     }
 
-  /* If we don't want a value, return NULL_RTX.  */
-  if ((want_value & 1) == 0)
-    return NULL_RTX;
-
-  /* If we are supposed to return TEMP, do so as long as it isn't a MEM.
-     ??? The latter test doesn't seem to make sense.  */
-  else if (dont_return_target && !MEM_P (temp))
-    return temp;
-
-  /* Return TARGET itself if it is a hard register.  */
-  else if ((want_value & 1) != 0
-          && GET_MODE (target) != BLKmode
-          && ! (REG_P (target)
-                && REGNO (target) < FIRST_PSEUDO_REGISTER))
-    return copy_to_reg (target);
-
-  else
-    return target;
+  return NULL_RTX;
 }
 \f
-/* Examine CTOR.  Discover how many scalar fields are set to nonzero
-   values and place it in *P_NZ_ELTS.  Discover how many scalar fields
-   are set to non-constant values and place it in  *P_NC_ELTS.  */
+/* Examine CTOR to discover:
+   * how many scalar fields are set to nonzero values,
+     and place it in *P_NZ_ELTS;
+   * how many scalar fields are set to non-constant values,
+     and place it in  *P_NC_ELTS; and
+   * how many scalar fields in total are in CTOR,
+     and place it in *P_ELT_COUNT.
+   * if a type is a union, and the initializer from the constructor
+     is not the largest element in the union, then set *p_must_clear.  */
 
 static void
 categorize_ctor_elements_1 (tree ctor, HOST_WIDE_INT *p_nz_elts,
-                           HOST_WIDE_INT *p_nc_elts)
+                           HOST_WIDE_INT *p_nc_elts,
+                           HOST_WIDE_INT *p_elt_count,
+                           bool *p_must_clear)
 {
-  HOST_WIDE_INT nz_elts, nc_elts;
-  tree list;
+  unsigned HOST_WIDE_INT idx;
+  HOST_WIDE_INT nz_elts, nc_elts, elt_count;
+  tree value, purpose;
 
   nz_elts = 0;
   nc_elts = 0;
+  elt_count = 0;
 
-  for (list = CONSTRUCTOR_ELTS (ctor); list; list = TREE_CHAIN (list))
+  FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), idx, purpose, value)
     {
-      tree value = TREE_VALUE (list);
-      tree purpose = TREE_PURPOSE (list);
       HOST_WIDE_INT mult;
 
       mult = 1;
@@ -4147,10 +4427,11 @@ categorize_ctor_elements_1 (tree ctor, HOST_WIDE_INT *p_nz_elts,
        {
        case CONSTRUCTOR:
          {
-           HOST_WIDE_INT nz = 0, nc = 0;
-           categorize_ctor_elements_1 (value, &nz, &nc);
+           HOST_WIDE_INT nz = 0, nc = 0, ic = 0;
+           categorize_ctor_elements_1 (value, &nz, &nc, &ic, p_must_clear);
            nz_elts += mult * nz;
            nc_elts += mult * nc;
+           elt_count += mult * ic;
          }
          break;
 
@@ -4158,48 +4439,104 @@ categorize_ctor_elements_1 (tree ctor, HOST_WIDE_INT *p_nz_elts,
        case REAL_CST:
          if (!initializer_zerop (value))
            nz_elts += mult;
+         elt_count += mult;
+         break;
+
+       case STRING_CST:
+         nz_elts += mult * TREE_STRING_LENGTH (value);
+         elt_count += mult * TREE_STRING_LENGTH (value);
          break;
+
        case COMPLEX_CST:
          if (!initializer_zerop (TREE_REALPART (value)))
            nz_elts += mult;
          if (!initializer_zerop (TREE_IMAGPART (value)))
            nz_elts += mult;
+         elt_count += mult;
          break;
+
        case VECTOR_CST:
          {
            tree v;
            for (v = TREE_VECTOR_CST_ELTS (value); v; v = TREE_CHAIN (v))
-             if (!initializer_zerop (TREE_VALUE (v)))
-               nz_elts += mult;
+             {
+               if (!initializer_zerop (TREE_VALUE (v)))
+                 nz_elts += mult;
+               elt_count += mult;
+             }
          }
          break;
 
        default:
          nz_elts += mult;
+         elt_count += mult;
          if (!initializer_constant_valid_p (value, TREE_TYPE (value)))
            nc_elts += mult;
          break;
        }
     }
 
+  if (!*p_must_clear
+      && (TREE_CODE (TREE_TYPE (ctor)) == UNION_TYPE
+         || TREE_CODE (TREE_TYPE (ctor)) == QUAL_UNION_TYPE))
+    {
+      tree init_sub_type;
+      bool clear_this = true;
+
+      if (!VEC_empty (constructor_elt, CONSTRUCTOR_ELTS (ctor)))
+       {
+         /* We don't expect more than one element of the union to be
+            initialized.  Not sure what we should do otherwise... */
+          gcc_assert (VEC_length (constructor_elt, CONSTRUCTOR_ELTS (ctor))
+                     == 1);
+
+          init_sub_type = TREE_TYPE (VEC_index (constructor_elt,
+                                               CONSTRUCTOR_ELTS (ctor),
+                                               0)->value);
+
+         /* ??? We could look at each element of the union, and find the
+            largest element.  Which would avoid comparing the size of the
+            initialized element against any tail padding in the union.
+            Doesn't seem worth the effort...  */
+         if (simple_cst_equal (TYPE_SIZE (TREE_TYPE (ctor)), 
+                               TYPE_SIZE (init_sub_type)) == 1)
+           {
+             /* And now we have to find out if the element itself is fully
+                constructed.  E.g. for union { struct { int a, b; } s; } u
+                = { .s = { .a = 1 } }.  */
+             if (elt_count == count_type_elements (init_sub_type, false))
+               clear_this = false;
+           }
+       }
+
+      *p_must_clear = clear_this;
+    }
+
   *p_nz_elts += nz_elts;
   *p_nc_elts += nc_elts;
+  *p_elt_count += elt_count;
 }
 
 void
 categorize_ctor_elements (tree ctor, HOST_WIDE_INT *p_nz_elts,
-                         HOST_WIDE_INT *p_nc_elts)
+                         HOST_WIDE_INT *p_nc_elts,
+                         HOST_WIDE_INT *p_elt_count,
+                         bool *p_must_clear)
 {
   *p_nz_elts = 0;
   *p_nc_elts = 0;
-  categorize_ctor_elements_1 (ctor, p_nz_elts, p_nc_elts);
+  *p_elt_count = 0;
+  *p_must_clear = false;
+  categorize_ctor_elements_1 (ctor, p_nz_elts, p_nc_elts, p_elt_count,
+                             p_must_clear);
 }
 
 /* Count the number of scalars in TYPE.  Return -1 on overflow or
-   variable-sized.  */
+   variable-sized.  If ALLOW_FLEXARR is true, don't count flexible
+   array member at the end of the structure.  */
 
 HOST_WIDE_INT
-count_type_elements (tree type)
+count_type_elements (tree type, bool allow_flexarr)
 {
   const HOST_WIDE_INT max = ~((HOST_WIDE_INT)1 << (HOST_BITS_PER_WIDE_INT-1));
   switch (TREE_CODE (type))
@@ -4210,7 +4547,7 @@ count_type_elements (tree type)
        if (telts && host_integerp (telts, 1))
          {
            HOST_WIDE_INT n = tree_low_cst (telts, 1) + 1;
-           HOST_WIDE_INT m = count_type_elements (TREE_TYPE (type));
+           HOST_WIDE_INT m = count_type_elements (TREE_TYPE (type), false);
            if (n == 0)
              return 0;
            else if (max / n > m)
@@ -4227,9 +4564,23 @@ count_type_elements (tree type)
        for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f))
          if (TREE_CODE (f) == FIELD_DECL)
            {
-             t = count_type_elements (TREE_TYPE (f));
+             t = count_type_elements (TREE_TYPE (f), false);
              if (t < 0)
-               return -1;
+               {
+                 /* Check for structures with flexible array member.  */
+                 tree tf = TREE_TYPE (f);
+                 if (allow_flexarr
+                     && TREE_CHAIN (f) == NULL
+                     && TREE_CODE (tf) == ARRAY_TYPE
+                     && TYPE_DOMAIN (tf)
+                     && TYPE_MIN_VALUE (TYPE_DOMAIN (tf))
+                     && integer_zerop (TYPE_MIN_VALUE (TYPE_DOMAIN (tf)))
+                     && !TYPE_MAX_VALUE (TYPE_DOMAIN (tf))
+                     && int_size_in_bytes (type) >= 0)
+                   break;
+
+                 return -1;
+               }
              n += t;
            }
 
@@ -4265,8 +4616,6 @@ count_type_elements (tree type)
 
     case VOID_TYPE:
     case METHOD_TYPE:
-    case FILE_TYPE:
-    case SET_TYPE:
     case FUNCTION_TYPE:
     case LANG_TYPE:
     default:
@@ -4276,20 +4625,20 @@ count_type_elements (tree type)
 
 /* Return 1 if EXP contains mostly (3/4)  zeros.  */
 
-int
+static int
 mostly_zeros_p (tree exp)
 {
   if (TREE_CODE (exp) == CONSTRUCTOR)
 
     {
-      HOST_WIDE_INT nz_elts, nc_elts, elts;
+      HOST_WIDE_INT nz_elts, nc_elts, count, elts;
+      bool must_clear;
 
-      /* If there are no ranges of true bits, it is all zero.  */
-      if (TREE_TYPE (exp) && TREE_CODE (TREE_TYPE (exp)) == SET_TYPE)
-       return CONSTRUCTOR_ELTS (exp) == NULL_TREE;
+      categorize_ctor_elements (exp, &nz_elts, &nc_elts, &count, &must_clear);
+      if (must_clear)
+       return 1;
 
-      categorize_ctor_elements (exp, &nz_elts, &nc_elts);
-      elts = count_type_elements (TREE_TYPE (exp));
+      elts = count_type_elements (TREE_TYPE (exp), false);
 
       return nz_elts < elts / 4;
     }
@@ -4343,8 +4692,7 @@ store_constructor_field (rtx target, unsigned HOST_WIDE_INT bitsize,
       store_constructor (exp, target, cleared, bitsize / BITS_PER_UNIT);
     }
   else
-    store_field (target, bitsize, bitpos, mode, exp, VOIDmode, 0, type,
-                alias_set);
+    store_field (target, bitsize, bitpos, mode, exp, type, alias_set);
 }
 
 /* Store the value of constructor EXP into the rtx TARGET.
@@ -4369,7 +4717,8 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
     case UNION_TYPE:
     case QUAL_UNION_TYPE:
       {
-       tree elt;
+       unsigned HOST_WIDE_INT idx;
+       tree field, value;
 
        /* If size is zero or the target is already cleared, do nothing.  */
        if (size == 0 || cleared)
@@ -4380,7 +4729,7 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
                 && ! CONSTRUCTOR_ELTS (exp))
          /* If the constructor is empty, clear the union.  */
          {
-           clear_storage (target, expr_size (exp));
+           clear_storage (target, expr_size (exp), BLOCK_OP_NORMAL);
            cleared = 1;
          }
 
@@ -4401,14 +4750,14 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
           register whose mode size isn't equal to SIZE since
           clear_storage can't handle this case.  */
        else if (size > 0
-                && ((list_length (CONSTRUCTOR_ELTS (exp))
+                && (((int)VEC_length (constructor_elt, CONSTRUCTOR_ELTS (exp))
                      != fields_length (type))
                     || mostly_zeros_p (exp))
                 && (!REG_P (target)
                     || ((HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (target))
                         == size)))
          {
-           clear_storage (target, GEN_INT (size));
+           clear_storage (target, GEN_INT (size), BLOCK_OP_NORMAL);
            cleared = 1;
          }
 
@@ -4417,11 +4766,8 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
 
        /* Store each element of the constructor into the
           corresponding field of TARGET.  */
-
-       for (elt = CONSTRUCTOR_ELTS (exp); elt; elt = TREE_CHAIN (elt))
+       FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (exp), idx, field, value)
          {
-           tree field = TREE_PURPOSE (elt);
-           tree value = TREE_VALUE (elt);
            enum machine_mode mode;
            HOST_WIDE_INT bitsize;
            HOST_WIDE_INT bitpos = 0;
@@ -4504,9 +4850,9 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
                
                if (BYTES_BIG_ENDIAN)
                  value
-                   = fold (build2 (LSHIFT_EXPR, type, value,
-                                   build_int_cst (NULL_TREE,
-                                                  BITS_PER_WORD - bitsize)));
+                  = fold_build2 (LSHIFT_EXPR, type, value,
+                                  build_int_cst (NULL_TREE,
+                                                 BITS_PER_WORD - bitsize));
                bitsize = BITS_PER_WORD;
                mode = word_mode;
              }
@@ -4527,8 +4873,8 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
       }
     case ARRAY_TYPE:
       {
-       tree elt;
-       int i;
+       tree value, index;
+       unsigned HOST_WIDE_INT i;
        int need_to_clear;
        tree domain;
        tree elttype = TREE_TYPE (type);
@@ -4558,18 +4904,20 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
          need_to_clear = 1;
        else
          {
+           unsigned HOST_WIDE_INT idx;
+           tree index, value;
            HOST_WIDE_INT count = 0, zero_count = 0;
            need_to_clear = ! const_bounds_p;
            
            /* This loop is a more accurate version of the loop in
               mostly_zeros_p (it handles RANGE_EXPR in an index).  It
               is also needed to check for missing elements.  */
-           for (elt = CONSTRUCTOR_ELTS (exp);
-                elt != NULL_TREE && ! need_to_clear;
-                elt = TREE_CHAIN (elt))
+           FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (exp), idx, index, value)
              {
-               tree index = TREE_PURPOSE (elt);
                HOST_WIDE_INT this_node_count;
+
+               if (need_to_clear)
+                 break;
                
                if (index != NULL_TREE && TREE_CODE (index) == RANGE_EXPR)
                  {
@@ -4590,7 +4938,7 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
                  this_node_count = 1;
                
                count += this_node_count;
-               if (mostly_zeros_p (TREE_VALUE (elt)))
+               if (mostly_zeros_p (value))
                  zero_count += this_node_count;
              }
            
@@ -4608,7 +4956,7 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
            if (REG_P (target))
              emit_move_insn (target,  CONST0_RTX (GET_MODE (target)));
            else
-             clear_storage (target, GEN_INT (size));
+             clear_storage (target, GEN_INT (size), BLOCK_OP_NORMAL);
            cleared = 1;
          }
 
@@ -4619,16 +4967,12 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
        /* Store each element of the constructor into the
           corresponding element of TARGET, determined by counting the
           elements.  */
-       for (elt = CONSTRUCTOR_ELTS (exp), i = 0;
-            elt;
-            elt = TREE_CHAIN (elt), i++)
+       FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (exp), i, index, value)
          {
            enum machine_mode mode;
            HOST_WIDE_INT bitsize;
            HOST_WIDE_INT bitpos;
            int unsignedp;
-           tree value = TREE_VALUE (elt);
-           tree index = TREE_PURPOSE (elt);
            rtx xtarget = target;
            
            if (cleared && initializer_zerop (value))
@@ -4707,8 +5051,8 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
                    /* Assign value to element index.  */
                    position
                      = convert (ssizetype,
-                                fold (build2 (MINUS_EXPR, TREE_TYPE (index),
-                                              index, TYPE_MIN_VALUE (domain))));
+                                fold_build2 (MINUS_EXPR, TREE_TYPE (index),
+                                             index, TYPE_MIN_VALUE (domain)));
                    position = size_binop (MULT_EXPR, position,
                                           convert (ssizetype,
                                                    TYPE_SIZE_UNIT (elttype)));
@@ -4732,7 +5076,7 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
                       the loop.  */
                    expand_assignment (index,
                                       build2 (PLUS_EXPR, TREE_TYPE (index),
-                                              index, integer_one_node), 0);
+                                              index, integer_one_node));
                    
                    emit_jump (loop_start);
                    
@@ -4750,10 +5094,10 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
                
                if (minelt)
                  index = fold_convert (ssizetype,
-                                       fold (build2 (MINUS_EXPR,
-                                                     TREE_TYPE (index),
-                                                     index,
-                                                     TYPE_MIN_VALUE (domain))));
+                                       fold_build2 (MINUS_EXPR,
+                                                    TREE_TYPE (index),
+                                                    index,
+                                                    TYPE_MIN_VALUE (domain)));
                
                position = size_binop (MULT_EXPR, index,
                                       convert (ssizetype,
@@ -4788,7 +5132,8 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
 
     case VECTOR_TYPE:
       {
-       tree elt;
+       unsigned HOST_WIDE_INT idx;
+       constructor_elt *ce;
        int i;
        int need_to_clear;
        int icode = 0;
@@ -4797,7 +5142,7 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
        enum machine_mode eltmode = TYPE_MODE (elttype);
        HOST_WIDE_INT bitsize;
        HOST_WIDE_INT bitpos;
-       rtx *vector = NULL;
+       rtvec vector = NULL;
        unsigned n_elts;
        
        gcc_assert (eltmode != BLKmode);
@@ -4812,9 +5157,9 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
              {
                unsigned int i;
                
-               vector = alloca (n_elts);
+               vector = rtvec_alloc (n_elts);
                for (i = 0; i < n_elts; i++)
-                 vector [i] = CONST0_RTX (GET_MODE_INNER (mode));
+                 RTVEC_ELT (vector, i) = CONST0_RTX (GET_MODE_INNER (mode));
              }
          }
        
@@ -4828,18 +5173,17 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
        else
          {
            unsigned HOST_WIDE_INT count = 0, zero_count = 0;
+           tree value;
            
-           for (elt = CONSTRUCTOR_ELTS (exp);
-                elt != NULL_TREE;
-                elt = TREE_CHAIN (elt))
+           FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, value)
              {
                int n_elts_here = tree_low_cst
                  (int_const_binop (TRUNC_DIV_EXPR,
-                                   TYPE_SIZE (TREE_TYPE (TREE_VALUE (elt))),
+                                   TYPE_SIZE (TREE_TYPE (value)),
                                    TYPE_SIZE (elttype), 0), 1);
                
                count += n_elts_here;
-               if (mostly_zeros_p (TREE_VALUE (elt)))
+               if (mostly_zeros_p (value))
                  zero_count += n_elts_here;
              }
 
@@ -4853,30 +5197,29 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
            if (REG_P (target))
              emit_move_insn (target,  CONST0_RTX (GET_MODE (target)));
            else
-             clear_storage (target, GEN_INT (size));
+             clear_storage (target, GEN_INT (size), BLOCK_OP_NORMAL);
            cleared = 1;
          }
        
+       /* Inform later passes that the old value is dead.  */
        if (!cleared && REG_P (target))
-         /* Inform later passes that the old value is dead.  */
-         emit_insn (gen_rtx_CLOBBER (VOIDmode, target));
+         emit_move_insn (target, CONST0_RTX (GET_MODE (target)));
 
         /* Store each element of the constructor into the corresponding
           element of TARGET, determined by counting the elements.  */
-       for (elt = CONSTRUCTOR_ELTS (exp), i = 0;
-            elt;
-            elt = TREE_CHAIN (elt), i += bitsize / elt_size)
+       for (idx = 0, i = 0;
+            VEC_iterate (constructor_elt, CONSTRUCTOR_ELTS (exp), idx, ce);
+            idx++, i += bitsize / elt_size)
          {
-           tree value = TREE_VALUE (elt);
-           tree index = TREE_PURPOSE (elt);
            HOST_WIDE_INT eltpos;
+           tree value = ce->value;
            
            bitsize = tree_low_cst (TYPE_SIZE (TREE_TYPE (value)), 1);
            if (cleared && initializer_zerop (value))
              continue;
            
-           if (index != 0)
-             eltpos = tree_low_cst (index, 1);
+           if (ce->index)
+             eltpos = tree_low_cst (ce->index, 1);
            else
              eltpos = i;
            
@@ -4885,7 +5228,8 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
                /* Vector CONSTRUCTORs should only be built from smaller
                   vectors in the case of BLKmode vectors.  */
                gcc_assert (TREE_CODE (TREE_TYPE (value)) != VECTOR_TYPE);
-               vector[eltpos] = expand_expr (value, NULL_RTX, VOIDmode, 0);
+               RTVEC_ELT (vector, eltpos)
+                 = expand_expr (value, NULL_RTX, VOIDmode, 0);
              }
            else
              {
@@ -4903,188 +5247,10 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
        if (vector)
          emit_insn (GEN_FCN (icode)
                     (target,
-                     gen_rtx_PARALLEL (GET_MODE (target),
-                                       gen_rtvec_v (n_elts, vector))));
-       break;
-      }
-
-      /* Set constructor assignments.  */
-    case SET_TYPE:
-      {
-       tree elt = CONSTRUCTOR_ELTS (exp);
-       unsigned HOST_WIDE_INT nbytes = int_size_in_bytes (type), nbits;
-       tree domain = TYPE_DOMAIN (type);
-       tree domain_min, domain_max, bitlength;
-       
-       /* The default implementation strategy is to extract the
-          constant parts of the constructor, use that to initialize
-          the target, and then "or" in whatever non-constant ranges
-          we need in addition.
-
-          If a large set is all zero or all ones, it is probably
-          better to set it using memset.  Also, if a large set has
-          just a single range, it may also be better to first clear
-          all the first clear the set (using memset), and set the
-          bits we want.  */
-
-       /* Check for all zeros.  */
-       if (elt == NULL_TREE && size > 0)
-         {
-           if (!cleared)
-             clear_storage (target, GEN_INT (size));
-           return;
-         }
-       
-       domain_min = convert (sizetype, TYPE_MIN_VALUE (domain));
-       domain_max = convert (sizetype, TYPE_MAX_VALUE (domain));
-       bitlength = size_binop (PLUS_EXPR,
-                               size_diffop (domain_max, domain_min),
-                               ssize_int (1));
-       
-       nbits = tree_low_cst (bitlength, 1);
-
-        /* For "small" sets, or "medium-sized" (up to 32 bytes) sets
-          that are "complicated" (more than one range), initialize
-          (the constant parts) by copying from a constant.  */
-       if (GET_MODE (target) != BLKmode || nbits <= 2 * BITS_PER_WORD
-           || (nbytes <= 32 && TREE_CHAIN (elt) != NULL_TREE))
-         {
-           unsigned int set_word_size = TYPE_ALIGN (TREE_TYPE (exp));
-           enum machine_mode mode = mode_for_size (set_word_size, MODE_INT, 1);
-           char *bit_buffer = alloca (nbits);
-           HOST_WIDE_INT word = 0;
-           unsigned int bit_pos = 0;
-           unsigned int ibit = 0;
-           unsigned int offset = 0;  /* In bytes from beginning of set.  */
-           
-           elt = get_set_constructor_bits (exp, bit_buffer, nbits);
-           for (;;)
-             {
-               if (bit_buffer[ibit])
-                 {
-                   if (BYTES_BIG_ENDIAN)
-                     word |= (1 << (set_word_size - 1 - bit_pos));
-                   else
-                     word |= 1 << bit_pos;
-                 }
-               
-               bit_pos++;  ibit++;
-               if (bit_pos >= set_word_size || ibit == nbits)
-                 {
-                   if (word != 0 || ! cleared)
-                     {
-                       rtx datum = gen_int_mode (word, mode);
-                       rtx to_rtx;
-                       
-                       /* The assumption here is that it is safe to
-                          use XEXP if the set is multi-word, but not
-                          if it's single-word.  */
-                       if (MEM_P (target))
-                         to_rtx = adjust_address (target, mode, offset);
-                       else
-                         {
-                           gcc_assert (!offset);
-                           to_rtx = target;
-                         }
-                       emit_move_insn (to_rtx, datum);
-                     }
-                   
-                   if (ibit == nbits)
-                     break;
-                   word = 0;
-                   bit_pos = 0;
-                   offset += set_word_size / BITS_PER_UNIT;
-                 }
-             }
-         }
-       else if (!cleared)
-         /* Don't bother clearing storage if the set is all ones.  */
-         if (TREE_CHAIN (elt) != NULL_TREE
-             || (TREE_PURPOSE (elt) == NULL_TREE
-                 ? nbits != 1
-                 : ( ! host_integerp (TREE_VALUE (elt), 0)
-                     || ! host_integerp (TREE_PURPOSE (elt), 0)
-                     || (tree_low_cst (TREE_VALUE (elt), 0)
-                         - tree_low_cst (TREE_PURPOSE (elt), 0) + 1
-                         != (HOST_WIDE_INT) nbits))))
-           clear_storage (target, expr_size (exp));
-       
-       for (; elt != NULL_TREE; elt = TREE_CHAIN (elt))
-         {
-           /* Start of range of element or NULL.  */
-           tree startbit = TREE_PURPOSE (elt);
-           /* End of range of element, or element value.  */
-           tree endbit   = TREE_VALUE (elt);
-           HOST_WIDE_INT startb, endb;
-           rtx bitlength_rtx, startbit_rtx, endbit_rtx, targetx;
-           
-           bitlength_rtx = expand_expr (bitlength,
-                                        NULL_RTX, MEM, EXPAND_CONST_ADDRESS);
-           
-           /* Handle non-range tuple element like [ expr ].  */
-           if (startbit == NULL_TREE)
-             {
-               startbit = save_expr (endbit);
-               endbit = startbit;
-             }
-           
-           startbit = convert (sizetype, startbit);
-           endbit = convert (sizetype, endbit);
-           if (! integer_zerop (domain_min))
-             {
-               startbit = size_binop (MINUS_EXPR, startbit, domain_min);
-               endbit = size_binop (MINUS_EXPR, endbit, domain_min);
-             }
-           startbit_rtx = expand_expr (startbit, NULL_RTX, MEM,
-                                       EXPAND_CONST_ADDRESS);
-           endbit_rtx = expand_expr (endbit, NULL_RTX, MEM,
-                                     EXPAND_CONST_ADDRESS);
-           
-           if (REG_P (target))
-             {
-               targetx
-                 = assign_temp
-                 ((build_qualified_type (lang_hooks.types.type_for_mode
-                                         (GET_MODE (target), 0),
-                                         TYPE_QUAL_CONST)),
-                  0, 1, 1);
-               emit_move_insn (targetx, target);
-             }
-           
-           else
-             {
-               gcc_assert (MEM_P (target));
-               targetx = target;
-             }
-
-           /* Optimization:  If startbit and endbit are constants divisible
-              by BITS_PER_UNIT, call memset instead.  */
-           if (TREE_CODE (startbit) == INTEGER_CST
-               && TREE_CODE (endbit) == INTEGER_CST
-               && (startb = TREE_INT_CST_LOW (startbit)) % BITS_PER_UNIT == 0
-               && (endb = TREE_INT_CST_LOW (endbit) + 1) % BITS_PER_UNIT == 0)
-             {
-               emit_library_call (memset_libfunc, LCT_NORMAL,
-                                  VOIDmode, 3,
-                                  plus_constant (XEXP (targetx, 0),
-                                                 startb / BITS_PER_UNIT),
-                                  Pmode,
-                                  constm1_rtx, TYPE_MODE (integer_type_node),
-                                  GEN_INT ((endb - startb) / BITS_PER_UNIT),
-                                  TYPE_MODE (sizetype));
-             }
-           else
-             emit_library_call (setbits_libfunc, LCT_NORMAL,
-                                VOIDmode, 4, XEXP (targetx, 0),
-                                Pmode, bitlength_rtx, TYPE_MODE (sizetype),
-                                startbit_rtx, TYPE_MODE (sizetype),
-                                endbit_rtx, TYPE_MODE (sizetype));
-           
-           if (REG_P (target))
-             emit_move_insn (target, targetx);
-         }
+                     gen_rtx_PARALLEL (GET_MODE (target), vector)));
        break;
       }
+      
     default:
       gcc_unreachable ();
     }
@@ -5095,12 +5261,8 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
    BITSIZE bits, starting BITPOS bits from the start of TARGET.
    If MODE is VOIDmode, it means that we are storing into a bit-field.
 
-   If VALUE_MODE is VOIDmode, return nothing in particular.
-   UNSIGNEDP is not used in this case.
-
-   Otherwise, return an rtx for the value stored.  This rtx
-   has mode VALUE_MODE if that is convenient to do.
-   In this case, UNSIGNEDP must be nonzero if the value is an unsigned type.
+   Always return const0_rtx unless we have something particular to
+   return.
 
    TYPE is the type of the underlying object,
 
@@ -5110,8 +5272,7 @@ store_constructor (tree exp, rtx target, int cleared, HOST_WIDE_INT size)
 
 static rtx
 store_field (rtx target, HOST_WIDE_INT bitsize, HOST_WIDE_INT bitpos,
-            enum machine_mode mode, tree exp, enum machine_mode value_mode,
-            int unsignedp, tree type, int alias_set)
+            enum machine_mode mode, tree exp, tree type, int alias_set)
 {
   HOST_WIDE_INT width_mask = 0;
 
@@ -5146,8 +5307,7 @@ store_field (rtx target, HOST_WIDE_INT bitsize, HOST_WIDE_INT bitpos,
       if (bitsize != (HOST_WIDE_INT) GET_MODE_BITSIZE (GET_MODE (target)))
        emit_move_insn (object, target);
 
-      store_field (blk_object, bitsize, bitpos, mode, exp, VOIDmode, 0, type,
-                  alias_set);
+      store_field (blk_object, bitsize, bitpos, mode, exp, type, alias_set);
 
       emit_move_insn (target, object);
 
@@ -5160,7 +5320,7 @@ store_field (rtx target, HOST_WIDE_INT bitsize, HOST_WIDE_INT bitpos,
       /* We're storing into a struct containing a single __complex.  */
 
       gcc_assert (!bitpos);
-      return store_expr (exp, target, value_mode != VOIDmode);
+      return store_expr (exp, target, 0);
     }
 
   /* If the structure is in a register or if the component
@@ -5187,7 +5347,27 @@ store_field (rtx target, HOST_WIDE_INT bitsize, HOST_WIDE_INT bitpos,
          && TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) == INTEGER_CST
          && compare_tree_int (TYPE_SIZE (TREE_TYPE (exp)), bitsize) != 0))
     {
-      rtx temp = expand_expr (exp, NULL_RTX, VOIDmode, 0);
+      rtx temp;
+
+      /* If EXP is a NOP_EXPR of precision less than its mode, then that
+        implies a mask operation.  If the precision is the same size as
+        the field we're storing into, that mask is redundant.  This is
+        particularly common with bit field assignments generated by the
+        C front end.  */
+      if (TREE_CODE (exp) == NOP_EXPR)
+       {
+         tree type = TREE_TYPE (exp);
+         if (INTEGRAL_TYPE_P (type)
+             && TYPE_PRECISION (type) < GET_MODE_BITSIZE (TYPE_MODE (type))
+             && bitsize == TYPE_PRECISION (type))
+           {
+             type = TREE_TYPE (TREE_OPERAND (exp, 0));
+             if (INTEGRAL_TYPE_P (type) && TYPE_PRECISION (type) >= bitsize)
+               exp = TREE_OPERAND (exp, 0);
+           }
+       }
+
+      temp = expand_expr (exp, NULL_RTX, VOIDmode, 0);
 
       /* If BITSIZE is narrower than the size of the type of EXP
         we will be narrowing TEMP.  Normally, what's wanted are the
@@ -5221,62 +5401,18 @@ store_field (rtx target, HOST_WIDE_INT bitsize, HOST_WIDE_INT bitpos,
                                    / BITS_PER_UNIT),
                           BLOCK_OP_NORMAL);
 
-         return value_mode == VOIDmode ? const0_rtx : target;
+         return const0_rtx;
        }
 
       /* Store the value in the bitfield.  */
       store_bit_field (target, bitsize, bitpos, mode, temp);
 
-      if (value_mode != VOIDmode)
-       {
-         /* The caller wants an rtx for the value.
-            If possible, avoid refetching from the bitfield itself.  */
-         if (width_mask != 0
-             && ! (MEM_P (target) && MEM_VOLATILE_P (target)))
-           {
-             tree count;
-             enum machine_mode tmode;
-
-             tmode = GET_MODE (temp);
-             if (tmode == VOIDmode)
-               tmode = value_mode;
-
-             if (unsignedp)
-               return expand_and (tmode, temp,
-                                  gen_int_mode (width_mask, tmode),
-                                  NULL_RTX);
-
-             count = build_int_cst (NULL_TREE,
-                                    GET_MODE_BITSIZE (tmode) - bitsize);
-             temp = expand_shift (LSHIFT_EXPR, tmode, temp, count, 0, 0);
-             return expand_shift (RSHIFT_EXPR, tmode, temp, count, 0, 0);
-           }
-
-         return extract_bit_field (target, bitsize, bitpos, unsignedp,
-                                   NULL_RTX, value_mode, VOIDmode);
-       }
       return const0_rtx;
     }
   else
     {
-      rtx addr = XEXP (target, 0);
-      rtx to_rtx = target;
-
-      /* If a value is wanted, it must be the lhs;
-        so make the address stable for multiple use.  */
-
-      if (value_mode != VOIDmode && !REG_P (addr)
-         && ! CONSTANT_ADDRESS_P (addr)
-         /* A frame-pointer reference is already stable.  */
-         && ! (GET_CODE (addr) == PLUS
-               && GET_CODE (XEXP (addr, 1)) == CONST_INT
-               && (XEXP (addr, 0) == virtual_incoming_args_rtx
-                   || XEXP (addr, 0) == virtual_stack_vars_rtx)))
-       to_rtx = replace_equiv_address (to_rtx, copy_to_reg (addr));
-
       /* Now build a reference to just the desired component.  */
-
-      to_rtx = adjust_address (target, mode, bitpos / BITS_PER_UNIT);
+      rtx to_rtx = adjust_address (target, mode, bitpos / BITS_PER_UNIT);
 
       if (to_rtx == target)
        to_rtx = copy_rtx (to_rtx);
@@ -5285,7 +5421,7 @@ store_field (rtx target, HOST_WIDE_INT bitsize, HOST_WIDE_INT bitpos,
       if (!MEM_KEEP_ALIAS_SET_P (to_rtx) && MEM_ALIAS_SET (to_rtx) != 0)
        set_mem_alias_set (to_rtx, alias_set);
 
-      return store_expr (exp, to_rtx, value_mode != VOIDmode);
+      return store_expr (exp, to_rtx, 0);
     }
 }
 \f
@@ -5309,13 +5445,27 @@ store_field (rtx target, HOST_WIDE_INT bitsize, HOST_WIDE_INT bitpos,
 
    If the field describes a variable-sized object, *PMODE is set to
    VOIDmode and *PBITSIZE is set to -1.  An access cannot be made in
-   this case, but the address of the object can be found.  */
+   this case, but the address of the object can be found.
+
+   If KEEP_ALIGNING is true and the target is STRICT_ALIGNMENT, we don't
+   look through nodes that serve as markers of a greater alignment than
+   the one that can be deduced from the expression.  These nodes make it
+   possible for front-ends to prevent temporaries from being created by
+   the middle-end on alignment considerations.  For that purpose, the
+   normal operating mode at high-level is to always pass FALSE so that
+   the ultimate containing object is really returned; moreover, the
+   associated predicate handled_component_p will always return TRUE
+   on these nodes, thus indicating that they are essentially handled
+   by get_inner_reference.  TRUE should only be passed when the caller
+   is scanning the expression in order to build another representation
+   and specifically knows how to handle these nodes; as such, this is
+   the normal operating mode in the RTL expanders.  */
 
 tree
 get_inner_reference (tree exp, HOST_WIDE_INT *pbitsize,
                     HOST_WIDE_INT *pbitpos, tree *poffset,
                     enum machine_mode *pmode, int *punsignedp,
-                    int *pvolatilep)
+                    int *pvolatilep, bool keep_aligning)
 {
   tree size_tree = 0;
   enum machine_mode mode = VOIDmode;
@@ -5361,72 +5511,84 @@ get_inner_reference (tree exp, HOST_WIDE_INT *pbitsize,
      and find the ultimate containing object.  */
   while (1)
     {
-      if (TREE_CODE (exp) == BIT_FIELD_REF)
-       bit_offset = size_binop (PLUS_EXPR, bit_offset, TREE_OPERAND (exp, 2));
-      else if (TREE_CODE (exp) == COMPONENT_REF)
+      switch (TREE_CODE (exp))
        {
-         tree field = TREE_OPERAND (exp, 1);
-         tree this_offset = component_ref_field_offset (exp);
+       case BIT_FIELD_REF:
+         bit_offset = size_binop (PLUS_EXPR, bit_offset,
+                                  TREE_OPERAND (exp, 2));
+         break;
 
-         /* If this field hasn't been filled in yet, don't go
-            past it.  This should only happen when folding expressions
-            made during type construction.  */
-         if (this_offset == 0)
-           break;
+       case COMPONENT_REF:
+         {
+           tree field = TREE_OPERAND (exp, 1);
+           tree this_offset = component_ref_field_offset (exp);
+
+           /* If this field hasn't been filled in yet, don't go past it.
+              This should only happen when folding expressions made during
+              type construction.  */
+           if (this_offset == 0)
+             break;
+
+           offset = size_binop (PLUS_EXPR, offset, this_offset);
+           bit_offset = size_binop (PLUS_EXPR, bit_offset,
+                                    DECL_FIELD_BIT_OFFSET (field));
 
-         offset = size_binop (PLUS_EXPR, offset, this_offset);
+           /* ??? Right now we don't do anything with DECL_OFFSET_ALIGN.  */
+         }
+         break;
+
+       case ARRAY_REF:
+       case ARRAY_RANGE_REF:
+         {
+           tree index = TREE_OPERAND (exp, 1);
+           tree low_bound = array_ref_low_bound (exp);
+           tree unit_size = array_ref_element_size (exp);
+
+           /* We assume all arrays have sizes that are a multiple of a byte.
+              First subtract the lower bound, if any, in the type of the
+              index, then convert to sizetype and multiply by the size of
+              the array element.  */
+           if (! integer_zerop (low_bound))
+             index = fold_build2 (MINUS_EXPR, TREE_TYPE (index),
+                                  index, low_bound);
+
+           offset = size_binop (PLUS_EXPR, offset,
+                                size_binop (MULT_EXPR,
+                                            convert (sizetype, index),
+                                            unit_size));
+         }
+         break;
+
+       case REALPART_EXPR:
+         break;
+
+       case IMAGPART_EXPR:
          bit_offset = size_binop (PLUS_EXPR, bit_offset,
-                                  DECL_FIELD_BIT_OFFSET (field));
+                                  bitsize_int (*pbitsize));
+         break;
 
-         /* ??? Right now we don't do anything with DECL_OFFSET_ALIGN.  */
-       }
+       case VIEW_CONVERT_EXPR:
+         if (keep_aligning && STRICT_ALIGNMENT
+             && (TYPE_ALIGN (TREE_TYPE (exp))
+              > TYPE_ALIGN (TREE_TYPE (TREE_OPERAND (exp, 0))))
+             && (TYPE_ALIGN (TREE_TYPE (TREE_OPERAND (exp, 0)))
+                 < BIGGEST_ALIGNMENT)
+             && (TYPE_ALIGN_OK (TREE_TYPE (exp))
+                 || TYPE_ALIGN_OK (TREE_TYPE (TREE_OPERAND (exp, 0)))))
+           goto done;
+         break;
 
-      else if (TREE_CODE (exp) == ARRAY_REF
-              || TREE_CODE (exp) == ARRAY_RANGE_REF)
-       {
-         tree index = TREE_OPERAND (exp, 1);
-         tree low_bound = array_ref_low_bound (exp);
-         tree unit_size = array_ref_element_size (exp);
-
-         /* We assume all arrays have sizes that are a multiple of a byte.
-            First subtract the lower bound, if any, in the type of the
-            index, then convert to sizetype and multiply by the size of the
-            array element.  */
-         if (! integer_zerop (low_bound))
-           index = fold (build2 (MINUS_EXPR, TREE_TYPE (index),
-                                 index, low_bound));
-
-         offset = size_binop (PLUS_EXPR, offset,
-                              size_binop (MULT_EXPR,
-                                          convert (sizetype, index),
-                                          unit_size));
+       default:
+         goto done;
        }
 
-      /* We can go inside most conversions: all NON_VALUE_EXPRs, all normal
-        conversions that don't change the mode, and all view conversions
-        except those that need to "step up" the alignment.  */
-      else if (TREE_CODE (exp) != NON_LVALUE_EXPR
-              && ! (TREE_CODE (exp) == VIEW_CONVERT_EXPR
-                    && ! ((TYPE_ALIGN (TREE_TYPE (exp))
-                           > TYPE_ALIGN (TREE_TYPE (TREE_OPERAND (exp, 0))))
-                          && STRICT_ALIGNMENT
-                          && (TYPE_ALIGN (TREE_TYPE (TREE_OPERAND (exp, 0)))
-                              < BIGGEST_ALIGNMENT)
-                          && (TYPE_ALIGN_OK (TREE_TYPE (exp))
-                              || TYPE_ALIGN_OK (TREE_TYPE
-                                                (TREE_OPERAND (exp, 0))))))
-              && ! ((TREE_CODE (exp) == NOP_EXPR
-                     || TREE_CODE (exp) == CONVERT_EXPR)
-                    && (TYPE_MODE (TREE_TYPE (exp))
-                        == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))))
-       break;
-
       /* If any reference in the chain is volatile, the effect is volatile.  */
       if (TREE_THIS_VOLATILE (exp))
        *pvolatilep = 1;
 
       exp = TREE_OPERAND (exp, 0);
     }
+ done:
 
   /* If OFFSET is constant, see if we can return the whole thing as a
      constant bit position.  Otherwise, split it up.  */
@@ -5547,18 +5709,11 @@ handled_component_p (tree t)
     case COMPONENT_REF:
     case ARRAY_REF:
     case ARRAY_RANGE_REF:
-    case NON_LVALUE_EXPR:
     case VIEW_CONVERT_EXPR:
+    case REALPART_EXPR:
+    case IMAGPART_EXPR:
       return 1;
 
-    /* ??? Sure they are handled, but get_inner_reference may return
-       a different PBITSIZE, depending upon whether the expression is
-       wrapped up in a NOP_EXPR or not, e.g. for bitfields.  */
-    case NOP_EXPR:
-    case CONVERT_EXPR:
-      return (TYPE_MODE (TREE_TYPE (t))
-             == TYPE_MODE (TREE_TYPE (TREE_OPERAND (t, 0))));
-
     default:
       return 0;
     }
@@ -5755,14 +5910,14 @@ safe_from_p (rtx x, tree exp, int top_p)
   /* Now look at our tree code and possibly recurse.  */
   switch (TREE_CODE_CLASS (TREE_CODE (exp)))
     {
-    case 'd':
+    case tcc_declaration:
       exp_rtl = DECL_RTL_IF_SET (exp);
       break;
 
-    case 'c':
+    case tcc_constant:
       return 1;
 
-    case 'x':
+    case tcc_exceptional:
       if (TREE_CODE (exp) == TREE_LIST)
        {
          while (1)
@@ -5781,7 +5936,7 @@ safe_from_p (rtx x, tree exp, int top_p)
       else
        return 0;
 
-    case 's':
+    case tcc_statement:
       /* The only case we look at here is the DECL_INITIAL inside a
         DECL_EXPR.  */
       return (TREE_CODE (exp) != DECL_EXPR
@@ -5789,17 +5944,17 @@ safe_from_p (rtx x, tree exp, int top_p)
              || !DECL_INITIAL (DECL_EXPR_DECL (exp))
              || safe_from_p (x, DECL_INITIAL (DECL_EXPR_DECL (exp)), 0));
 
-    case '2':
-    case '<':
+    case tcc_binary:
+    case tcc_comparison:
       if (!safe_from_p (x, TREE_OPERAND (exp, 1), 0))
        return 0;
       /* Fall through.  */
 
-    case '1':
+    case tcc_unary:
       return safe_from_p (x, TREE_OPERAND (exp, 0), 0);
 
-    case 'e':
-    case 'r':
+    case tcc_expression:
+    case tcc_reference:
       /* Now do code-specific tests.  EXP_RTL is set to any rtx we find in
         the expression.  If it is set, we conflict iff we are that rtx or
         both are in memory.  Otherwise, we check all operands of the
@@ -5829,6 +5984,8 @@ safe_from_p (rtx x, tree exp, int top_p)
            }
          break;
 
+       case MISALIGNED_INDIRECT_REF:
+       case ALIGN_INDIRECT_REF:
        case INDIRECT_REF:
          if (MEM_P (x)
              && alias_sets_conflict_p (MEM_ALIAS_SET (x),
@@ -5860,7 +6017,7 @@ safe_from_p (rtx x, tree exp, int top_p)
       if (exp_rtl)
        break;
 
-      nops = first_rtl_op (TREE_CODE (exp));
+      nops = TREE_CODE_LENGTH (TREE_CODE (exp));
       for (i = 0; i < nops; i++)
        if (TREE_OPERAND (exp, i) != 0
            && ! safe_from_p (x, TREE_OPERAND (exp, i), 0))
@@ -5872,6 +6029,11 @@ safe_from_p (rtx x, tree exp, int top_p)
          >= (unsigned int) LAST_AND_UNUSED_TREE_CODE
          && !lang_hooks.safe_from_p (x, exp))
        return 0;
+      break;
+
+    case tcc_type:
+      /* Should never get a type here.  */
+      gcc_unreachable ();
     }
 
   /* If we have an rtl, find any enclosed object.  Then see if we conflict
@@ -5902,7 +6064,7 @@ safe_from_p (rtx x, tree exp, int top_p)
 /* Return the highest power of two that EXP is known to be a multiple of.
    This is used in updating alignment of MEMs in array references.  */
 
-static unsigned HOST_WIDE_INT
+unsigned HOST_WIDE_INT
 highest_pow2_factor (tree exp)
 {
   unsigned HOST_WIDE_INT c0, c1;
@@ -6003,7 +6165,7 @@ expand_var (tree var)
       ? !TREE_ASM_WRITTEN (var)
       : !DECL_RTL_SET_P (var))
     {
-      if (TREE_CODE (var) == VAR_DECL && DECL_VALUE_EXPR (var))
+      if (TREE_CODE (var) == VAR_DECL && DECL_HAS_VALUE_EXPR_P (var))
        /* Should be ignored.  */;
       else if (lang_hooks.expand_decl (var))
        /* OK.  */;
@@ -6068,7 +6230,7 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enum machine_mode tmode,
      generating ADDR_EXPR of something that isn't an LVALUE.  The only
      exception here is STRING_CST.  */
   if (TREE_CODE (exp) == CONSTRUCTOR
-      || TREE_CODE_CLASS (TREE_CODE (exp)) == 'c')
+      || CONSTANT_CLASS_P (exp))
     return XEXP (output_constant_def (exp, 0), 0);
 
   /* Everything must be something allowed by is_gimple_addressable.  */
@@ -6081,7 +6243,7 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enum machine_mode tmode,
     case CONST_DECL:
       /* Recurse and make the output_constant_def clause above handle this.  */
       return expand_expr_addr_expr_1 (DECL_INITIAL (exp), target,
-                                   tmode, modifier);
+                                     tmode, modifier);
 
     case REALPART_EXPR:
       /* The real part of the complex number is always first, therefore
@@ -6115,11 +6277,11 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enum machine_mode tmode,
          /* If the DECL isn't in memory, then the DECL wasn't properly
             marked TREE_ADDRESSABLE, which will be either a front-end
             or a tree optimizer bug.  */
-         gcc_assert (GET_CODE (result) == MEM);
+         gcc_assert (MEM_P (result));
          result = XEXP (result, 0);
 
          /* ??? Is this needed anymore?  */
-         if (!TREE_USED (exp) == 0)
+         if (DECL_P (exp) && !TREE_USED (exp) == 0)
            {
              assemble_external (exp);
              TREE_USED (exp) = 1;
@@ -6131,8 +6293,13 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enum machine_mode tmode,
          return result;
        }
 
+      /* Pass FALSE as the last argument to get_inner_reference although
+        we are expanding to RTL.  The rationale is that we know how to
+        handle "aligning nodes" here: we can just bypass them because
+        they won't change the final object whose address will be returned
+        (they actually exist only for that purpose).  */
       inner = get_inner_reference (exp, &bitsize, &bitpos, &offset,
-                                  &mode1, &unsignedp, &volatilep);
+                                  &mode1, &unsignedp, &volatilep, false);
       break;
     }
 
@@ -6142,13 +6309,6 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enum machine_mode tmode,
   subtarget = offset || bitpos ? NULL_RTX : target;
   result = expand_expr_addr_expr_1 (inner, subtarget, tmode, modifier);
 
-  if (tmode == VOIDmode)
-    {
-      tmode = GET_MODE (result);
-      if (tmode == VOIDmode)
-       tmode = Pmode;
-    }
-
   if (offset)
     {
       rtx tmp;
@@ -6157,6 +6317,9 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enum machine_mode tmode,
        result = force_operand (result, NULL);
       tmp = expand_expr (offset, NULL, tmode, EXPAND_NORMAL);
 
+      result = convert_memory_address (tmode, result);
+      tmp = convert_memory_address (tmode, tmp);
+
       if (modifier == EXPAND_SUM)
        result = gen_rtx_PLUS (tmode, result, tmp);
       else
@@ -6171,7 +6334,7 @@ expand_expr_addr_expr_1 (tree exp, rtx target, enum machine_mode tmode,
     {
       /* Someone beforehand should have rejected taking the address
         of such an object.  */
-      gcc_assert (!(bitpos % BITS_PER_UNIT));
+      gcc_assert ((bitpos % BITS_PER_UNIT) == 0);
 
       result = plus_constant (result, bitpos / BITS_PER_UNIT);
       if (modifier < EXPAND_SUM)
@@ -6191,19 +6354,28 @@ expand_expr_addr_expr (tree exp, rtx target, enum machine_mode tmode,
   enum machine_mode rmode;
   rtx result;
 
+  /* Target mode of VOIDmode says "whatever's natural".  */
+  if (tmode == VOIDmode)
+    tmode = TYPE_MODE (TREE_TYPE (exp));
+
+  /* We can get called with some Weird Things if the user does silliness
+     like "(short) &a".  In that case, convert_memory_address won't do
+     the right thing, so ignore the given target mode.  */
+  if (tmode != Pmode && tmode != ptr_mode)
+    tmode = Pmode;
+
   result = expand_expr_addr_expr_1 (TREE_OPERAND (exp, 0), target,
                                    tmode, modifier);
 
   /* Despite expand_expr claims concerning ignoring TMODE when not
-     strictly convenient, stuff breaks if we don't honor it.  */
-  if (tmode == VOIDmode)
-    tmode = TYPE_MODE (TREE_TYPE (exp));
+     strictly convenient, stuff breaks if we don't honor it.  Note
+     that combined with the above, we only do this for pointer modes.  */
   rmode = GET_MODE (result);
   if (rmode == VOIDmode)
     rmode = tmode;
   if (rmode != tmode)
     result = convert_memory_address (tmode, result);
+
   return result;
 }
 
@@ -6400,24 +6572,19 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
          return const0_rtx;
        }
 
-      if (TREE_CODE_CLASS (code) == '1' || code == COMPONENT_REF
-         || code == INDIRECT_REF)
+      if (TREE_CODE_CLASS (code) == tcc_unary
+         || code == COMPONENT_REF || code == INDIRECT_REF)
        return expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode,
                            modifier);
 
-      else if (TREE_CODE_CLASS (code) == '2' || TREE_CODE_CLASS (code) == '<'
+      else if (TREE_CODE_CLASS (code) == tcc_binary
+              || TREE_CODE_CLASS (code) == tcc_comparison
               || code == ARRAY_REF || code == ARRAY_RANGE_REF)
        {
          expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, modifier);
          expand_expr (TREE_OPERAND (exp, 1), const0_rtx, VOIDmode, modifier);
          return const0_rtx;
        }
-      else if ((code == TRUTH_ANDIF_EXPR || code == TRUTH_ORIF_EXPR)
-              && ! TREE_SIDE_EFFECTS (TREE_OPERAND (exp, 1)))
-       /* If the second operand has no side effects, just evaluate
-          the first.  */
-       return expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode,
-                           modifier);
       else if (code == BIT_FIELD_REF)
        {
          expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, modifier);
@@ -6429,18 +6596,6 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
       target = 0;
     }
 
-  /* If will do cse, generate all results into pseudo registers
-     since 1) that allows cse to find more things
-     and 2) otherwise cse could produce an insn the machine
-     cannot support.  An exception is a CONSTRUCTOR into a multi-word
-     MEM: that's much more likely to be most efficient into the MEM.
-     Another is a CALL_EXPR which must return in memory.  */
-
-  if (! cse_not_expected && mode != BLKmode && target
-      && (!REG_P (target) || REGNO (target) < FIRST_PSEUDO_REGISTER)
-      && ! (code == CONSTRUCTOR && GET_MODE_SIZE (mode) > UNITS_PER_WORD)
-      && ! (code == CALL_EXPR && aggregate_value_p (exp, exp)))
-    target = 0;
 
   switch (code)
     {
@@ -6579,8 +6734,9 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
          || GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (exp))) == MODE_VECTOR_FLOAT)
        return const_vector_from_tree (exp);
       else
-       return expand_expr (build1 (CONSTRUCTOR, TREE_TYPE (exp),
-                                   TREE_VECTOR_CST_ELTS (exp)),
+       return expand_expr (build_constructor_from_list
+                           (TREE_TYPE (exp),
+                            TREE_VECTOR_CST_ELTS (exp)),
                            ignore ? const0_rtx : target, tmode, modifier);
 
     case CONST_DECL:
@@ -6678,10 +6834,11 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
         subexpressions.  */
       if (ignore)
        {
-         tree elt;
+         unsigned HOST_WIDE_INT idx;
+         tree value;
 
-         for (elt = CONSTRUCTOR_ELTS (exp); elt; elt = TREE_CHAIN (elt))
-           expand_expr (TREE_VALUE (elt), const0_rtx, VOIDmode, 0);
+         FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), idx, value)
+           expand_expr (value, const0_rtx, VOIDmode, 0);
 
          return const0_rtx;
        }
@@ -6739,10 +6896,11 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
          return target;
        }
 
+    case MISALIGNED_INDIRECT_REF:
+    case ALIGN_INDIRECT_REF:
     case INDIRECT_REF:
       {
        tree exp1 = TREE_OPERAND (exp, 0);
-       tree orig;
 
        if (modifier != EXPAND_WRITE)
          {
@@ -6755,36 +6913,65 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
 
        op0 = expand_expr (exp1, NULL_RTX, VOIDmode, EXPAND_SUM);
        op0 = memory_address (mode, op0);
+
+       if (code == ALIGN_INDIRECT_REF)
+         {
+           int align = TYPE_ALIGN_UNIT (type);
+           op0 = gen_rtx_AND (Pmode, op0, GEN_INT (-align));
+           op0 = memory_address (mode, op0);
+         }
+
        temp = gen_rtx_MEM (mode, op0);
 
-       orig = REF_ORIGINAL (exp);
-       if (!orig)
-         orig = exp;
-       set_mem_attributes (temp, orig, 0);
+       set_mem_attributes (temp, exp, 0);
+
+       /* Resolve the misalignment now, so that we don't have to remember
+          to resolve it later.  Of course, this only works for reads.  */
+       /* ??? When we get around to supporting writes, we'll have to handle
+          this in store_expr directly.  The vectorizer isn't generating
+          those yet, however.  */
+       if (code == MISALIGNED_INDIRECT_REF)
+         {
+           int icode;
+           rtx reg, insn;
+
+           gcc_assert (modifier == EXPAND_NORMAL);
+
+           /* The vectorizer should have already checked the mode.  */
+           icode = movmisalign_optab->handlers[mode].insn_code;
+           gcc_assert (icode != CODE_FOR_nothing);
+
+           /* We've already validated the memory, and we're creating a
+              new pseudo destination.  The predicates really can't fail.  */
+           reg = gen_reg_rtx (mode);
+
+           /* Nor can the insn generator.  */
+           insn = GEN_FCN (icode) (reg, temp);
+           emit_insn (insn);
+
+           return reg;
+         }
 
        return temp;
       }
 
-    case ARRAY_REF:
-
+    case TARGET_MEM_REF:
       {
-       tree array = TREE_OPERAND (exp, 0);
-       tree low_bound = array_ref_low_bound (exp);
-       tree index = convert (sizetype, TREE_OPERAND (exp, 1));
-       HOST_WIDE_INT i;
-
-       gcc_assert (TREE_CODE (TREE_TYPE (array)) == ARRAY_TYPE);
+       struct mem_address addr;
 
-       /* Optimize the special-case of a zero lower bound.
+       get_address_description (exp, &addr);
+       op0 = addr_for_mem_ref (&addr, true);
+       op0 = memory_address (mode, op0);
+       temp = gen_rtx_MEM (mode, op0);
+       set_mem_attributes (temp, TMR_ORIGINAL (exp), 0);
+      }
+      return temp;
 
-          We convert the low_bound to sizetype to avoid some problems
-          with constant folding.  (E.g. suppose the lower bound is 1,
-          and its mode is QI.  Without the conversion,  (ARRAY
-          +(INDEX-(unsigned char)1)) becomes ((ARRAY+(-(unsigned char)1))
-          +INDEX), which becomes (ARRAY+255+INDEX).  Oops!)  */
+    case ARRAY_REF:
 
-       if (! integer_zerop (low_bound))
-         index = size_diffop (index, convert (sizetype, low_bound));
+      {
+       tree array = TREE_OPERAND (exp, 0);
+       tree index = TREE_OPERAND (exp, 1);
 
        /* Fold an expression like: "foo"[2].
           This is not done in fold so it won't happen inside &.
@@ -6811,21 +6998,19 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
            && modifier != EXPAND_MEMORY
            && TREE_CODE (array) == CONSTRUCTOR
            && ! TREE_SIDE_EFFECTS (array)
-           && TREE_CODE (index) == INTEGER_CST
-           && 0 > compare_tree_int (index,
-                                    list_length (CONSTRUCTOR_ELTS
-                                                 (TREE_OPERAND (exp, 0)))))
+           && TREE_CODE (index) == INTEGER_CST)
          {
-           tree elem;
-
-           for (elem = CONSTRUCTOR_ELTS (TREE_OPERAND (exp, 0)),
-                i = TREE_INT_CST_LOW (index);
-                elem != 0 && i != 0; i--, elem = TREE_CHAIN (elem))
-             ;
+           unsigned HOST_WIDE_INT ix;
+           tree field, value;
 
-           if (elem)
-             return expand_expr (fold (TREE_VALUE (elem)), target, tmode,
-                                 modifier);
+           FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (array), ix,
+                                     field, value)
+             if (tree_int_cst_equal (field, index))
+               {
+                 if (!TREE_SIDE_EFFECTS (value))
+                   return expand_expr (fold (value), target, tmode, modifier);
+                 break;
+               }
          }
 
        else if (optimize >= 1
@@ -6843,17 +7028,18 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
 
                if (TREE_CODE (init) == CONSTRUCTOR)
                  {
-                   tree elem;
+                   unsigned HOST_WIDE_INT ix;
+                   tree field, value;
 
-                   for (elem = CONSTRUCTOR_ELTS (init);
-                        (elem
-                         && !tree_int_cst_equal (TREE_PURPOSE (elem), index));
-                        elem = TREE_CHAIN (elem))
-                     ;
-
-                   if (elem && !TREE_SIDE_EFFECTS (TREE_VALUE (elem)))
-                     return expand_expr (fold (TREE_VALUE (elem)), target,
-                                         tmode, modifier);
+                   FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), ix,
+                                             field, value)
+                     if (tree_int_cst_equal (field, index))
+                       {
+                         if (!TREE_SIDE_EFFECTS (value))
+                           return expand_expr (fold (value), target, tmode,
+                                               modifier);
+                         break;
+                       }
                  }
                else if (TREE_CODE (init) == STRING_CST
                         && 0 > compare_tree_int (index,
@@ -6877,11 +7063,12 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
         appropriate field if it is present.  */
       if (TREE_CODE (TREE_OPERAND (exp, 0)) == CONSTRUCTOR)
        {
-         tree elt;
+         unsigned HOST_WIDE_INT idx;
+         tree field, value;
 
-         for (elt = CONSTRUCTOR_ELTS (TREE_OPERAND (exp, 0)); elt;
-              elt = TREE_CHAIN (elt))
-           if (TREE_PURPOSE (elt) == TREE_OPERAND (exp, 1)
+         FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (TREE_OPERAND (exp, 0)),
+                                   idx, field, value)
+           if (field == TREE_OPERAND (exp, 1)
                /* We can normally use the value of the field in the
                   CONSTRUCTOR.  However, if this is a bitfield in
                   an integral mode that we can fit in a HOST_WIDE_INT,
@@ -6889,24 +7076,21 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
                   since this is done implicitly by the constructor.  If
                   the bitfield does not meet either of those conditions,
                   we can't do this optimization.  */
-               && (! DECL_BIT_FIELD (TREE_PURPOSE (elt))
-                   || ((GET_MODE_CLASS (DECL_MODE (TREE_PURPOSE (elt)))
-                        == MODE_INT)
-                       && (GET_MODE_BITSIZE (DECL_MODE (TREE_PURPOSE (elt)))
+               && (! DECL_BIT_FIELD (field)
+                   || ((GET_MODE_CLASS (DECL_MODE (field)) == MODE_INT)
+                       && (GET_MODE_BITSIZE (DECL_MODE (field))
                            <= HOST_BITS_PER_WIDE_INT))))
              {
-               if (DECL_BIT_FIELD (TREE_PURPOSE (elt))
+               if (DECL_BIT_FIELD (field)
                    && modifier == EXPAND_STACK_PARM)
                  target = 0;
-               op0 = expand_expr (TREE_VALUE (elt), target, tmode, modifier);
-               if (DECL_BIT_FIELD (TREE_PURPOSE (elt)))
+               op0 = expand_expr (value, target, tmode, modifier);
+               if (DECL_BIT_FIELD (field))
                  {
-                   HOST_WIDE_INT bitsize
-                     = TREE_INT_CST_LOW (DECL_SIZE (TREE_PURPOSE (elt)));
-                   enum machine_mode imode
-                     = TYPE_MODE (TREE_TYPE (TREE_PURPOSE (elt)));
+                   HOST_WIDE_INT bitsize = TREE_INT_CST_LOW (DECL_SIZE (field));
+                   enum machine_mode imode = TYPE_MODE (TREE_TYPE (field));
 
-                   if (TYPE_UNSIGNED (TREE_TYPE (TREE_PURPOSE (elt))))
+                   if (TYPE_UNSIGNED (TREE_TYPE (field)))
                      {
                        op1 = GEN_INT (((HOST_WIDE_INT) 1 << bitsize) - 1);
                        op0 = expand_and (imode, op0, op1, target);
@@ -6938,7 +7122,7 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
        tree offset;
        int volatilep = 0;
        tree tem = get_inner_reference (exp, &bitsize, &bitpos, &offset,
-                                       &mode1, &unsignedp, &volatilep);
+                                       &mode1, &unsignedp, &volatilep, true);
        rtx orig_op0;
 
        /* If we got back the original object, something is wrong.  Perhaps
@@ -7248,7 +7432,7 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
            /* Store data into beginning of memory target.  */
            store_expr (TREE_OPERAND (exp, 0),
                        adjust_address (target, TYPE_MODE (valtype), 0),
-                       modifier == EXPAND_STACK_PARM ? 2 : 0);
+                       modifier == EXPAND_STACK_PARM);
 
          else
            {
@@ -7261,7 +7445,7 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
                                 * BITS_PER_UNIT),
                                (HOST_WIDE_INT) GET_MODE_BITSIZE (mode)),
                           0, TYPE_MODE (valtype), TREE_OPERAND (exp, 0),
-                          VOIDmode, 0, type, 0);
+                          type, 0);
            }
 
          /* Return the entire union.  */
@@ -7284,36 +7468,39 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
        }
 
       op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, mode, modifier);
-      op0 = REDUCE_BIT_FIELD (op0);
       if (GET_MODE (op0) == mode)
-       return op0;
+       ;
 
       /* If OP0 is a constant, just convert it into the proper mode.  */
-      if (CONSTANT_P (op0))
+      else if (CONSTANT_P (op0))
        {
          tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
          enum machine_mode inner_mode = TYPE_MODE (inner_type);
 
          if (modifier == EXPAND_INITIALIZER)
-           return simplify_gen_subreg (mode, op0, inner_mode,
-                                       subreg_lowpart_offset (mode,
-                                                              inner_mode));
+           op0 = simplify_gen_subreg (mode, op0, inner_mode,
+                                      subreg_lowpart_offset (mode,
+                                                             inner_mode));
          else
-           return convert_modes (mode, inner_mode, op0,
-                                 TYPE_UNSIGNED (inner_type));
+           op0=  convert_modes (mode, inner_mode, op0,
+                                TYPE_UNSIGNED (inner_type));
        }
 
-      if (modifier == EXPAND_INITIALIZER)
-       return gen_rtx_fmt_e (unsignedp ? ZERO_EXTEND : SIGN_EXTEND, mode, op0);
+      else if (modifier == EXPAND_INITIALIZER)
+       op0 = gen_rtx_fmt_e (unsignedp ? ZERO_EXTEND : SIGN_EXTEND, mode, op0);
 
-      if (target == 0)
-       return
-         convert_to_mode (mode, op0,
-                          TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))));
+      else if (target == 0)
+       op0 = convert_to_mode (mode, op0,
+                              TYPE_UNSIGNED (TREE_TYPE
+                                             (TREE_OPERAND (exp, 0))));
       else
-       convert_move (target, op0,
-                     TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))));
-      return target;
+       {
+         convert_move (target, op0,
+                       TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))));
+         op0 = target;
+       }
+
+      return REDUCE_BIT_FIELD (op0);
 
     case VIEW_CONVERT_EXPR:
       op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, mode, modifier);
@@ -7615,7 +7802,7 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
          optab other_optab = zextend_p ? smul_widen_optab : umul_widen_optab;
          this_optab = zextend_p ? umul_widen_optab : smul_widen_optab;
 
-         if (mode == GET_MODE_WIDER_MODE (innermode))
+         if (mode == GET_MODE_2XWIDER_MODE (innermode))
            {
              if (this_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
                {
@@ -7674,18 +7861,6 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
       return expand_divmod (0, code, mode, op0, op1, target, unsignedp);
 
     case RDIV_EXPR:
-      /* Emit a/b as a*(1/b).  Later we may manage CSE the reciprocal saving
-         expensive divide.  If not, combine will rebuild the original
-         computation.  */
-      if (flag_unsafe_math_optimizations && optimize && !optimize_size
-         && TREE_CODE (type) == REAL_TYPE
-         && !real_onep (TREE_OPERAND (exp, 0)))
-        return expand_expr (build2 (MULT_EXPR, type, TREE_OPERAND (exp, 0),
-                                   build2 (RDIV_EXPR, type,
-                                           build_real (type, dconst1),
-                                           TREE_OPERAND (exp, 1))),
-                           target, tmode, modifier);
-
       goto binop;
 
     case TRUNC_MOD_EXPR:
@@ -7775,21 +7950,67 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
       /* At this point, a MEM target is no longer useful; we will get better
         code without it.  */
 
-      if (MEM_P (target))
+      if (! REG_P (target))
        target = gen_reg_rtx (mode);
 
       /* If op1 was placed in target, swap op0 and op1.  */
       if (target != op0 && target == op1)
        {
-         rtx tem = op0;
+         temp = op0;
          op0 = op1;
-         op1 = tem;
+         op1 = temp;
        }
 
+      /* We generate better code and avoid problems with op1 mentioning
+        target by forcing op1 into a pseudo if it isn't a constant.  */
+      if (! CONSTANT_P (op1))
+       op1 = force_reg (mode, op1);
+
+#ifdef HAVE_conditional_move
+      /* Use a conditional move if possible.  */
+      if (can_conditionally_move_p (mode))
+       {
+         enum rtx_code comparison_code;
+         rtx insn;
+
+         if (code == MAX_EXPR)
+           comparison_code = unsignedp ? GEU : GE;
+         else
+           comparison_code = unsignedp ? LEU : LE;
+
+         /* ??? Same problem as in expmed.c: emit_conditional_move
+            forces a stack adjustment via compare_from_rtx, and we
+            lose the stack adjustment if the sequence we are about
+            to create is discarded.  */
+         do_pending_stack_adjust ();
+
+         start_sequence ();
+
+         /* Try to emit the conditional move.  */
+         insn = emit_conditional_move (target, comparison_code,
+                                       op0, op1, mode,
+                                       op0, op1, mode,
+                                       unsignedp);
+
+         /* If we could do the conditional move, emit the sequence,
+            and return.  */
+         if (insn)
+           {
+             rtx seq = get_insns ();
+             end_sequence ();
+             emit_insn (seq);
+             return target;
+           }
+
+         /* Otherwise discard the sequence and fall back to code with
+            branches.  */
+         end_sequence ();
+       }
+#endif
       if (target != op0)
        emit_move_insn (target, op0);
 
-      op0 = gen_label_rtx ();
+      temp = gen_label_rtx ();
 
       /* If this mode is an integer too wide to compare properly,
         compare word by word.  Rely on cse to optimize constant cases.  */
@@ -7798,18 +8019,18 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
        {
          if (code == MAX_EXPR)
            do_jump_by_parts_greater_rtx (mode, unsignedp, target, op1,
-                                         NULL_RTX, op0);
+                                         NULL_RTX, temp);
          else
            do_jump_by_parts_greater_rtx (mode, unsignedp, op1, target,
-                                         NULL_RTX, op0);
+                                         NULL_RTX, temp);
        }
       else
        {
          do_compare_rtx_and_jump (target, op1, code == MAX_EXPR ? GE : LE,
-                                  unsignedp, mode, NULL_RTX, NULL_RTX, op0);
+                                  unsignedp, mode, NULL_RTX, NULL_RTX, temp);
        }
       emit_move_insn (target, op1);
-      emit_label (op0);
+      emit_label (temp);
       return target;
 
     case BIT_NOT_EXPR:
@@ -7969,21 +8190,10 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
       return const0_rtx;
 
     case COND_EXPR:
-      /* If it's void, we don't need to worry about computing a value.  */
-      if (VOID_TYPE_P (TREE_TYPE (exp)))
-       {
-         tree pred = TREE_OPERAND (exp, 0);
-         tree then_ = TREE_OPERAND (exp, 1);
-         tree else_ = TREE_OPERAND (exp, 2);
-
-         gcc_assert (TREE_CODE (then_) == GOTO_EXPR
-                     && TREE_CODE (GOTO_DESTINATION (then_)) == LABEL_DECL
-                     && TREE_CODE (else_) == GOTO_EXPR
-                     && TREE_CODE (GOTO_DESTINATION (else_)) == LABEL_DECL);
-
-         jumpif (pred, label_rtx (GOTO_DESTINATION (then_)));
-         return expand_expr (else_, const0_rtx, VOIDmode, 0);
-       }
+      /* A COND_EXPR with its type being VOID_TYPE represents a
+        conditional jump and is handled in
+        expand_gimple_cond_expr.  */
+      gcc_assert (!VOID_TYPE_P (TREE_TYPE (exp)));
 
         /* Note that COND_EXPRs whose type is a structure or union
         are required to be constructed to contain assignments of
@@ -8019,32 +8229,28 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
        op1 = gen_label_rtx ();
        jumpifnot (TREE_OPERAND (exp, 0), op0);
        store_expr (TREE_OPERAND (exp, 1), temp,
-                 modifier == EXPAND_STACK_PARM ? 2 : 0);
+                 modifier == EXPAND_STACK_PARM);
 
        emit_jump_insn (gen_jump (op1));
        emit_barrier ();
        emit_label (op0);
        store_expr (TREE_OPERAND (exp, 2), temp,
-                 modifier == EXPAND_STACK_PARM ? 2 : 0);
+                 modifier == EXPAND_STACK_PARM);
 
        emit_label (op1);
        OK_DEFER_POP;
        return temp;
 
+    case VEC_COND_EXPR:
+       target = expand_vec_cond_expr (exp, target);
+       return target;
+
     case MODIFY_EXPR:
       {
-       /* If lhs is complex, expand calls in rhs before computing it.
-          That's so we don't compute a pointer and save it over a
-          call.  If lhs is simple, compute it first so we can give it
-          as a target if the rhs is just a call.  This avoids an
-          extra temp and copy and that prevents a partial-subsumption
-          which makes bad code.  Actually we could treat
-          component_ref's of vars like vars.  */
-
        tree lhs = TREE_OPERAND (exp, 0);
        tree rhs = TREE_OPERAND (exp, 1);
 
-       temp = 0;
+       gcc_assert (ignore);
 
        /* Check for |= or &= of a bitfield of size one into another bitfield
           of size 1.  In this case, (unless we need the result of the
@@ -8054,8 +8260,7 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
           ??? At this point, we can't get a BIT_FIELD_REF here.  But if
           things change so we do, this code should be enhanced to
           support it.  */
-       if (ignore
-           && TREE_CODE (lhs) == COMPONENT_REF
+       if (TREE_CODE (lhs) == COMPONENT_REF
            && (TREE_CODE (rhs) == BIT_IOR_EXPR
                || TREE_CODE (rhs) == BIT_AND_EXPR)
            && TREE_OPERAND (rhs, 0) == lhs
@@ -8071,16 +8276,15 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
            expand_assignment (lhs, convert (TREE_TYPE (rhs),
                                             (TREE_CODE (rhs) == BIT_IOR_EXPR
                                              ? integer_one_node
-                                             : integer_zero_node)),
-                              0);
+                                             : integer_zero_node)));
            do_pending_stack_adjust ();
            emit_label (label);
            return const0_rtx;
          }
 
-       temp = expand_assignment (lhs, rhs, ! ignore);
+       expand_assignment (lhs, rhs);
 
-       return temp;
+       return const0_rtx;
       }
 
     case RETURN_EXPR:
@@ -8093,47 +8297,27 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
     case ADDR_EXPR:
       return expand_expr_addr_expr (exp, target, tmode, modifier);
 
-    /* COMPLEX type for Extended Pascal & Fortran  */
     case COMPLEX_EXPR:
-      {
-       enum machine_mode mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (exp)));
-       rtx insns;
-
-       /* Get the rtx code of the operands.  */
-       op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
-       op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0);
-
-       if (! target)
-         target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
-
-       start_sequence ();
-
-       /* Move the real (op0) and imaginary (op1) parts to their location.  */
-       emit_move_insn (gen_realpart (mode, target), op0);
-       emit_move_insn (gen_imagpart (mode, target), op1);
+      /* Get the rtx code of the operands.  */
+      op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
+      op1 = expand_expr (TREE_OPERAND (exp, 1), 0, VOIDmode, 0);
 
-       insns = get_insns ();
-       end_sequence ();
+      if (!target)
+       target = gen_reg_rtx (TYPE_MODE (TREE_TYPE (exp)));
 
-       /* Complex construction should appear as a single unit.  */
-       /* If TARGET is a CONCAT, we got insns like RD = RS, ID = IS,
-          each with a separate pseudo as destination.
-          It's not correct for flow to treat them as a unit.  */
-       if (GET_CODE (target) != CONCAT)
-         emit_no_conflict_block (insns, target, op0, op1, NULL_RTX);
-       else
-         emit_insn (insns);
+      /* Move the real (op0) and imaginary (op1) parts to their location.  */
+      write_complex_part (target, op0, false);
+      write_complex_part (target, op1, true);
 
-       return target;
-      }
+      return target;
 
     case REALPART_EXPR:
       op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
-      return gen_realpart (mode, op0);
+      return read_complex_part (op0, false);
 
     case IMAGPART_EXPR:
       op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
-      return gen_imagpart (mode, op0);
+      return read_complex_part (op0, true);
 
     case RESX_EXPR:
       expand_resx_expr (exp);
@@ -8161,8 +8345,6 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
     case POSTDECREMENT_EXPR:
     case LOOP_EXPR:
     case EXIT_EXPR:
-    case LABELED_BLOCK_EXPR:
-    case EXIT_BLOCK_EXPR:
     case TRUTH_ANDIF_EXPR:
     case TRUTH_ORIF_EXPR:
       /* Lowered by gimplify.c.  */
@@ -8197,6 +8379,40 @@ expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
       return expand_expr_real (TREE_OPERAND (exp, 0), original_target, tmode,
                               modifier, alt_rtl);
 
+    case REALIGN_LOAD_EXPR:
+      {
+        tree oprnd0 = TREE_OPERAND (exp, 0); 
+        tree oprnd1 = TREE_OPERAND (exp, 1);
+        tree oprnd2 = TREE_OPERAND (exp, 2);
+        rtx op2;
+
+        this_optab = optab_for_tree_code (code, type);
+        expand_operands (oprnd0, oprnd1, NULL_RTX, &op0, &op1, 0);
+        op2 = expand_expr (oprnd2, NULL_RTX, VOIDmode, 0);
+        temp = expand_ternary_op (mode, this_optab, op0, op1, op2, 
+                                 target, unsignedp);
+        gcc_assert (temp);
+        return temp;
+      }
+
+    case REDUC_MAX_EXPR:
+    case REDUC_MIN_EXPR:
+    case REDUC_PLUS_EXPR:
+      {
+        op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
+        this_optab = optab_for_tree_code (code, type);
+        temp = expand_unop (mode, this_optab, op0, target, unsignedp);
+        gcc_assert (temp);
+        return temp;
+      }
+
+    case VEC_LSHIFT_EXPR:
+    case VEC_RSHIFT_EXPR:
+      {
+       target = expand_vec_shift_expr (exp, target);
+       return target;
+      }
+
     default:
       return lang_hooks.expand_expr (exp, original_target, tmode,
                                     modifier, alt_rtl);
@@ -8577,8 +8793,7 @@ do_store_flag (tree exp, rtx target, enum machine_mode mode, int only_cheap)
       if ((code == LT && integer_zerop (arg1))
          || (! only_cheap && code == GE && integer_zerop (arg1)))
        ;
-      else if (BRANCH_COST >= 0
-              && ! only_cheap && (code == NE || code == EQ)
+      else if (! only_cheap && (code == NE || code == EQ)
               && TREE_CODE (type) != REAL_TYPE
               && ((abs_optab->handlers[(int) operand_mode].insn_code
                    != CODE_FOR_nothing)
@@ -8822,9 +9037,9 @@ try_tablejump (tree index_type, tree index_expr, tree minval, tree range,
   if (! HAVE_tablejump)
     return 0;
 
-  index_expr = fold (build2 (MINUS_EXPR, index_type,
-                            convert (index_type, index_expr),
-                            convert (index_type, minval)));
+  index_expr = fold_build2 (MINUS_EXPR, index_type,
+                           convert (index_type, index_expr),
+                           convert (index_type, minval));
   index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0);
   do_pending_stack_adjust ();