OSDN Git Service

(expand_builtin, case BUILT_IN_NEXT_ARG): Strip off INDIRECT_REF when
[pf3gnuchains/gcc-fork.git] / gcc / expr.c
index 364789d..342fa37 100644 (file)
@@ -1,5 +1,5 @@
 /* Convert tree expression to rtl instructions, for GNU compiler.
-   Copyright (C) 1988, 1992, 1993, 1994 Free Software Foundation, Inc.
+   Copyright (C) 1988, 92, 93, 94, 95, 1996 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -15,7 +15,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 
 #include "config.h"
@@ -25,6 +26,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "obstack.h"
 #include "flags.h"
 #include "regs.h"
+#include "hard-reg-set.h"
 #include "function.h"
 #include "insn-flags.h"
 #include "insn-codes.h"
@@ -118,10 +120,27 @@ struct move_by_pieces
   rtx to_addr;
   int autinc_to;
   int explicit_inc_to;
+  int to_struct;
   rtx from;
   rtx from_addr;
   int autinc_from;
   int explicit_inc_from;
+  int from_struct;
+  int len;
+  int offset;
+  int reverse;
+};
+
+/* This structure is used by clear_by_pieces to describe the clear to
+   be performed.  */
+
+struct clear_by_pieces
+{
+  rtx to;
+  rtx to_addr;
+  int autinc_to;
+  int explicit_inc_to;
+  int to_struct;
   int len;
   int offset;
   int reverse;
@@ -135,7 +154,7 @@ extern int local_vars_size;
 extern int stack_depth;
 extern int max_stack_depth;
 extern struct obstack permanent_obstack;
-
+extern rtx arg_pointer_save_area;
 
 static rtx enqueue_insn                PROTO((rtx, rtx));
 static int queued_subexp_p     PROTO((rtx));
@@ -144,7 +163,12 @@ static void move_by_pieces PROTO((rtx, rtx, int, int));
 static int move_by_pieces_ninsns PROTO((unsigned int, int));
 static void move_by_pieces_1   PROTO((rtx (*) (), enum machine_mode,
                                       struct move_by_pieces *));
-static void store_constructor  PROTO((tree, rtx));
+static void clear_by_pieces    PROTO((rtx, int, int));
+static void clear_by_pieces_1  PROTO((rtx (*) (), enum machine_mode,
+                                      struct clear_by_pieces *));
+static int is_zeros_p          PROTO((tree));
+static int mostly_zeros_p      PROTO((tree));
+static void store_constructor  PROTO((tree, rtx, int));
 static rtx store_field         PROTO((rtx, int, int, enum machine_mode, tree,
                                       enum machine_mode, int, int, int));
 static int get_inner_unaligned_p PROTO((tree));
@@ -180,7 +204,7 @@ void bc_load_localaddr              PROTO((rtx));
 void bc_load_parmaddr          PROTO((rtx));
 static void preexpand_calls    PROTO((tree));
 static void do_jump_by_parts_greater PROTO((tree, int, rtx, rtx));
-static void do_jump_by_parts_greater_rtx PROTO((enum machine_mode, int, rtx, rtx, rtx, rtx));
+void do_jump_by_parts_greater_rtx PROTO((enum machine_mode, int, rtx, rtx, rtx, rtx));
 static void do_jump_by_parts_equality PROTO((tree, rtx, rtx));
 static void do_jump_by_parts_equality_rtx PROTO((rtx, rtx, rtx));
 static void do_jump_for_compare        PROTO((rtx, rtx, rtx));
@@ -188,6 +212,7 @@ static rtx compare          PROTO((tree, enum rtx_code, enum rtx_code));
 static rtx do_store_flag       PROTO((tree, rtx, enum machine_mode, int));
 static tree defer_cleanups_to  PROTO((tree));
 extern void (*interim_eh_hook) PROTO((tree));
+extern tree truthvalue_conversion       PROTO((tree));
 
 /* 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
@@ -212,10 +237,13 @@ static char direct_store[NUM_MACHINE_MODES];
 /* This array records the insn_code of insns to perform block moves.  */
 enum insn_code movstr_optab[NUM_MACHINE_MODES];
 
+/* This array records the insn_code of insns to perform block clears.  */
+enum insn_code clrstr_optab[NUM_MACHINE_MODES];
+
 /* SLOW_UNALIGNED_ACCESS is non-zero if unaligned accesses are very slow. */
 
 #ifndef SLOW_UNALIGNED_ACCESS
-#define SLOW_UNALIGNED_ACCESS 0
+#define SLOW_UNALIGNED_ACCESS STRICT_ALIGNMENT
 #endif
 
 /* Register mappings for target machines without register windows.  */
@@ -916,6 +944,12 @@ convert_move (to, from, unsignedp)
       /* No special multiword conversion insn; do it by hand.  */
       start_sequence ();
 
+      /* Since we will turn this into a no conflict block, we must ensure
+        that the source does not overlap the target.  */
+
+      if (reg_overlap_mentioned_p (to, from))
+       from = force_reg (from_mode, from);
+
       /* Get a copy of FROM widened to a word, if necessary.  */
       if (GET_MODE_BITSIZE (from_mode) < BITS_PER_WORD)
        lowpart_mode = word_mode;
@@ -1075,6 +1109,9 @@ convert_move (to, from, unsignedp)
            || GET_CODE (from) == REG
            || GET_CODE (from) == SUBREG))
        from = force_reg (from_mode, from);
+      if (GET_CODE (from) == REG && REGNO (from) < FIRST_PSEUDO_REGISTER
+         && ! HARD_REGNO_MODE_OK (REGNO (from), to_mode))
+       from = copy_to_reg (from);
       emit_move_insn (to, gen_lowpart (to_mode, from));
       return;
     }
@@ -1086,12 +1123,6 @@ convert_move (to, from, unsignedp)
       if ((code = can_extend_p (to_mode, from_mode, unsignedp))
          != CODE_FOR_nothing)
        {
-         /* If FROM is a SUBREG, put it into a register.  Do this
-            so that we always generate the same set of insns for
-            better cse'ing; if an intermediate assignment occurred,
-            we won't be doing the operation directly on the SUBREG.  */
-         if (optimize > 0 && GET_CODE (from) == SUBREG)
-           from = force_reg (from_mode, from);
          emit_unop_insn (code, to, from, equiv_code);
          return;
        }
@@ -1409,6 +1440,9 @@ move_by_pieces (to, from, len, align)
   if (data.reverse) data.offset = len;
   data.len = len;
 
+  data.to_struct = MEM_IN_STRUCT_P (to);
+  data.from_struct = MEM_IN_STRUCT_P (from);
+
   /* If copying requires more than two move insns,
      copy addresses to registers (to make displacements shorter)
      and use post-increment if available.  */
@@ -1453,7 +1487,7 @@ move_by_pieces (to, from, len, align)
        data.to_addr = copy_addr_to_reg (to_addr);
     }
 
-  if (! (STRICT_ALIGNMENT || SLOW_UNALIGNED_ACCESS)
+  if (! SLOW_UNALIGNED_ACCESS
       || align > MOVE_MAX || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT)
     align = MOVE_MAX;
 
@@ -1498,7 +1532,7 @@ move_by_pieces_ninsns (l, align)
   register int n_insns = 0;
   int max_size = MOVE_MAX + 1;
 
-  if (! (STRICT_ALIGNMENT || SLOW_UNALIGNED_ACCESS)
+  if (! SLOW_UNALIGNED_ACCESS
       || align > MOVE_MAX || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT)
     align = MOVE_MAX;
 
@@ -1548,11 +1582,13 @@ move_by_pieces_1 (genfun, mode, data)
             ? gen_rtx (MEM, mode, data->to_addr)
             : change_address (data->to, mode,
                               plus_constant (data->to_addr, data->offset)));
+      MEM_IN_STRUCT_P (to1) = data->to_struct;
       from1 =
        (data->autinc_from
         ? gen_rtx (MEM, mode, data->from_addr)
         : change_address (data->from, mode,
                           plus_constant (data->from_addr, data->offset)));
+      MEM_IN_STRUCT_P (from1) = data->from_struct;
 
 #ifdef HAVE_PRE_DECREMENT
       if (data->explicit_inc_to < 0)
@@ -1630,7 +1666,10 @@ emit_block_move (x, y, size, align)
                 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.  */
-             && (unsigned HOST_WIDE_INT) INTVAL (size) <= GET_MODE_MASK (mode)
+             && ((GET_CODE (size) == CONST_INT
+                  && ((unsigned HOST_WIDE_INT) INTVAL (size)
+                      <= GET_MODE_MASK (mode)))
+                 || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD)
              && (insn_operand_predicate[(int) code][0] == 0
                  || (*insn_operand_predicate[(int) code][0]) (x, BLKmode))
              && (insn_operand_predicate[(int) code][1] == 0
@@ -1670,9 +1709,9 @@ emit_block_move (x, y, size, align)
       emit_library_call (bcopy_libfunc, 0,
                         VOIDmode, 3, XEXP (y, 0), Pmode,
                         XEXP (x, 0), Pmode,
-                        convert_to_mode (TYPE_MODE (sizetype), size,
-                                         TREE_UNSIGNED (sizetype)),
-                        TYPE_MODE (sizetype));
+                        convert_to_mode (TYPE_MODE (integer_type_node), size,
+                                         TREE_UNSIGNED (integer_type_node)),
+                        TYPE_MODE (integer_type_node));
 #endif
     }
 }
@@ -1813,27 +1852,224 @@ use_regs (call_fusage, regno, nregs)
     use_reg (call_fusage, gen_rtx (REG, reg_raw_mode[regno + i], regno + i));
 }
 \f
+/* Generate several move instructions to clear LEN bytes of block TO.
+   (A MEM rtx with BLKmode).   The caller must pass TO through
+   protect_from_queue before calling. ALIGN (in bytes) is maximum alignment
+   we can assume.  */
+
+static void
+clear_by_pieces (to, len, align)
+     rtx to;
+     int len, align;
+{
+  struct clear_by_pieces data;
+  rtx to_addr = XEXP (to, 0);
+  int max_size = MOVE_MAX + 1;
+
+  data.offset = 0;
+  data.to_addr = to_addr;
+  data.to = to;
+  data.autinc_to
+    = (GET_CODE (to_addr) == PRE_INC || GET_CODE (to_addr) == PRE_DEC
+       || GET_CODE (to_addr) == POST_INC || GET_CODE (to_addr) == POST_DEC);
+
+  data.explicit_inc_to = 0;
+  data.reverse
+    = (GET_CODE (to_addr) == PRE_DEC || GET_CODE (to_addr) == POST_DEC);
+  if (data.reverse) data.offset = len;
+  data.len = len;
+
+  data.to_struct = MEM_IN_STRUCT_P (to);
+
+  /* If copying requires more than two move insns,
+     copy addresses to registers (to make displacements shorter)
+     and use post-increment if available.  */
+  if (!data.autinc_to
+      && move_by_pieces_ninsns (len, align) > 2)
+    {
+#ifdef HAVE_PRE_DECREMENT
+      if (data.reverse && ! data.autinc_to)
+       {
+         data.to_addr = copy_addr_to_reg (plus_constant (to_addr, len));
+         data.autinc_to = 1;
+         data.explicit_inc_to = -1;
+       }
+#endif
+#ifdef HAVE_POST_INCREMENT
+      if (! data.reverse && ! data.autinc_to)
+       {
+         data.to_addr = copy_addr_to_reg (to_addr);
+         data.autinc_to = 1;
+         data.explicit_inc_to = 1;
+       }
+#endif
+      if (!data.autinc_to && CONSTANT_P (to_addr))
+       data.to_addr = copy_addr_to_reg (to_addr);
+    }
+
+  if (! SLOW_UNALIGNED_ACCESS
+      || align > MOVE_MAX || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT)
+    align = MOVE_MAX;
+
+  /* First move what we can in the largest integer mode, then go to
+     successively smaller modes.  */
+
+  while (max_size > 1)
+    {
+      enum machine_mode mode = VOIDmode, tmode;
+      enum insn_code icode;
+
+      for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+          tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
+       if (GET_MODE_SIZE (tmode) < max_size)
+         mode = tmode;
+
+      if (mode == VOIDmode)
+       break;
+
+      icode = mov_optab->handlers[(int) mode].insn_code;
+      if (icode != CODE_FOR_nothing
+         && align >= MIN (BIGGEST_ALIGNMENT / BITS_PER_UNIT,
+                          GET_MODE_SIZE (mode)))
+       clear_by_pieces_1 (GEN_FCN (icode), mode, &data);
+
+      max_size = GET_MODE_SIZE (mode);
+    }
+
+  /* The code above should have handled everything.  */
+  if (data.len != 0)
+    abort ();
+}
+
+/* Subroutine of clear_by_pieces.  Clear as many bytes as appropriate
+   with move instructions for mode MODE.  GENFUN is the gen_... function
+   to make a move insn for that mode.  DATA has all the other info.  */
+
+static void
+clear_by_pieces_1 (genfun, mode, data)
+     rtx (*genfun) ();
+     enum machine_mode mode;
+     struct clear_by_pieces *data;
+{
+  register int size = GET_MODE_SIZE (mode);
+  register rtx to1;
+
+  while (data->len >= size)
+    {
+      if (data->reverse) data->offset -= size;
+
+      to1 = (data->autinc_to
+            ? gen_rtx (MEM, mode, data->to_addr)
+            : change_address (data->to, mode,
+                              plus_constant (data->to_addr, data->offset)));
+      MEM_IN_STRUCT_P (to1) = data->to_struct;
+
+#ifdef HAVE_PRE_DECREMENT
+      if (data->explicit_inc_to < 0)
+       emit_insn (gen_add2_insn (data->to_addr, GEN_INT (-size)));
+#endif
+
+      emit_insn ((*genfun) (to1, const0_rtx));
+#ifdef HAVE_POST_INCREMENT
+      if (data->explicit_inc_to > 0)
+       emit_insn (gen_add2_insn (data->to_addr, GEN_INT (size)));
+#endif
+
+      if (! data->reverse) data->offset += size;
+
+      data->len -= size;
+    }
+}
+\f
 /* Write zeros through the storage of OBJECT.
-   If OBJECT has BLKmode, SIZE is its length in bytes.  */
+   If OBJECT has BLKmode, SIZE is its length in bytes and ALIGN is
+   the maximum alignment we can is has, measured in bytes.  */
 
 void
-clear_storage (object, size)
+clear_storage (object, size, align)
      rtx object;
-     int size;
+     rtx size;
+     int align;
 {
   if (GET_MODE (object) == BLKmode)
     {
+      object = protect_from_queue (object, 1);
+      size = protect_from_queue (size, 0);
+
+      if (GET_CODE (size) == CONST_INT
+         && (move_by_pieces_ninsns (INTVAL (size), align) < MOVE_RATIO))
+       clear_by_pieces (object, INTVAL (size), align);
+
+      else
+       {
+         /* 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);
+         enum machine_mode mode;
+
+         for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
+              mode = GET_MODE_WIDER_MODE (mode))
+           {
+             enum insn_code code = clrstr_optab[(int) mode];
+
+             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)))
+                     || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD)
+                 && (insn_operand_predicate[(int) code][0] == 0
+                     || (*insn_operand_predicate[(int) code][0]) (object,
+                                                                  BLKmode))
+                 && (insn_operand_predicate[(int) code][2] == 0
+                     || (*insn_operand_predicate[(int) code][2]) (opalign,
+                                                                  VOIDmode)))
+               {
+                 rtx op1;
+                 rtx last = get_last_insn ();
+                 rtx pat;
+
+                 op1 = convert_to_mode (mode, size, 1);
+                 if (insn_operand_predicate[(int) code][1] != 0
+                     && ! (*insn_operand_predicate[(int) code][1]) (op1,
+                                                                    mode))
+                   op1 = copy_to_mode_reg (mode, op1);
+
+                 pat = GEN_FCN ((int) code) (object, op1, opalign);
+                 if (pat)
+                   {
+                     emit_insn (pat);
+                     return;
+                   }
+                 else
+                   delete_insns_since (last);
+               }
+           }
+
+
 #ifdef TARGET_MEM_FUNCTIONS
-      emit_library_call (memset_libfunc, 0,
-                        VOIDmode, 3,
-                        XEXP (object, 0), Pmode, const0_rtx, Pmode,
-                        GEN_INT (size), Pmode);
+         emit_library_call (memset_libfunc, 0,
+                            VOIDmode, 3,
+                            XEXP (object, 0), Pmode,
+                            const0_rtx, TYPE_MODE (integer_type_node),
+                            convert_to_mode (TYPE_MODE (sizetype),
+                                             size, TREE_UNSIGNED (sizetype)),
+                            TYPE_MODE (sizetype));
 #else
-      emit_library_call (bzero_libfunc, 0,
-                        VOIDmode, 2,
-                        XEXP (object, 0), Pmode,
-                        GEN_INT (size), Pmode);
+         emit_library_call (bzero_libfunc, 0,
+                            VOIDmode, 2,
+                            XEXP (object, 0), Pmode,   
+                            convert_to_mode (TYPE_MODE (integer_type_node),
+                                             size,
+                                             TREE_UNSIGNED (integer_type_node)),
+                            TYPE_MODE (integer_type_node));
 #endif
+       }
     }
   else
     emit_move_insn (object, const0_rtx);
@@ -1957,6 +2193,21 @@ emit_move_insn_1 (x, y)
       rtx last_insn = 0;
       rtx insns;
       
+#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)))
+       {
+         anti_adjust_stack (GEN_INT (GET_MODE_SIZE (GET_MODE (x))));
+         x = change_address (x, VOIDmode, stack_pointer_rtx);
+       }
+#endif
+                            
+      /* Show the output dies here.  */
+      if (x != y)
+        emit_insn (gen_rtx (CLOBBER, VOIDmode, x));
+
       for (i = 0;
           i < (GET_MODE_SIZE (mode)  + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
           i++)
@@ -2004,6 +2255,8 @@ push_block (size, extra, below)
      int extra, below;
 {
   register rtx temp;
+
+  size = convert_modes (Pmode, ptr_mode, size, 1);
   if (CONSTANT_P (size))
     anti_adjust_stack (plus_constant (size, extra));
   else if (GET_CODE (size) == REG && extra == 0)
@@ -2147,7 +2400,7 @@ emit_push_insn (x, mode, type, size, align, partial, reg, extra,
          /* Here we avoid the case of a structure whose weak alignment
             forces many pushes of a small amount of data,
             and such small pushes do rounding that causes trouble.  */
-         && ((! STRICT_ALIGNMENT && ! SLOW_UNALIGNED_ACCESS)
+         && ((! SLOW_UNALIGNED_ACCESS)
              || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT
              || PUSH_ROUNDING (align) == align)
          && PUSH_ROUNDING (INTVAL (size)) == INTVAL (size))
@@ -2286,9 +2539,10 @@ emit_push_insn (x, mode, type, size, align, partial, reg, extra,
 #else
          emit_library_call (bcopy_libfunc, 0,
                             VOIDmode, 3, XEXP (xinner, 0), Pmode, temp, Pmode,
-                            convert_to_mode (TYPE_MODE (sizetype),
-                                             size, TREE_UNSIGNED (sizetype)),
-                            TYPE_MODE (sizetype));
+                            convert_to_mode (TYPE_MODE (integer_type_node),
+                                             size,
+                                             TREE_UNSIGNED (integer_type_node)),
+                            TYPE_MODE (integer_type_node));
 #endif
          OK_DEFER_POP;
        }
@@ -2450,7 +2704,7 @@ expand_assignment (to, from, want_value, suggest_reg)
       || (TREE_CODE (to) == ARRAY_REF
          && ((TREE_CODE (TREE_OPERAND (to, 1)) == INTEGER_CST
               && TREE_CODE (TYPE_SIZE (TREE_TYPE (to))) == INTEGER_CST)
-             || (STRICT_ALIGNMENT && get_inner_unaligned_p (to)))))
+             || (SLOW_UNALIGNED_ACCESS && get_inner_unaligned_p (to)))))
     {
       enum machine_mode mode1;
       int bitsize;
@@ -2480,8 +2734,8 @@ expand_assignment (to, from, want_value, suggest_reg)
          if (GET_CODE (to_rtx) != MEM)
            abort ();
          to_rtx = change_address (to_rtx, VOIDmode,
-                                  gen_rtx (PLUS, Pmode, XEXP (to_rtx, 0),
-                                           force_reg (Pmode, offset_rtx)));
+                                  gen_rtx (PLUS, ptr_mode, XEXP (to_rtx, 0),
+                                           force_reg (ptr_mode, offset_rtx)));
          /* If we have a variable offset, the known alignment
             is only that of the innermost structure containing the field.
             (Actually, we could sometimes do better by using the
@@ -2494,7 +2748,14 @@ expand_assignment (to, from, want_value, suggest_reg)
       if (volatilep)
        {
          if (GET_CODE (to_rtx) == MEM)
-           MEM_VOLATILE_P (to_rtx) = 1;
+           {
+             /* When the offset is zero, to_rtx is the address of the
+                structure we are storing into, and hence may be shared.
+                We must make a new MEM before setting the volatile bit.  */
+             if (offset == 0)
+               to_rtx = change_address (to_rtx, VOIDmode, XEXP (to_rtx, 0));
+             MEM_VOLATILE_P (to_rtx) = 1;
+           }
 #if 0  /* This was turned off because, when a field is volatile
          in an object which is not volatile, the object may be in a register,
          and then we would abort over here.  */
@@ -2544,7 +2805,12 @@ expand_assignment (to, from, want_value, suggest_reg)
       value = expand_expr (from, NULL_RTX, VOIDmode, 0);
       if (to_rtx == 0)
        to_rtx = expand_expr (to, NULL_RTX, VOIDmode, 0);
-      emit_move_insn (to_rtx, value);
+
+      if (GET_MODE (to_rtx) == BLKmode)
+       emit_block_move (to_rtx, value, expr_size (from),
+                        TYPE_ALIGN (TREE_TYPE (from)) / BITS_PER_UNIT);
+      else
+       emit_move_insn (to_rtx, value);
       preserve_temp_slots (to_rtx);
       free_temp_slots ();
       pop_temp_slots ();
@@ -2595,9 +2861,9 @@ expand_assignment (to, from, want_value, suggest_reg)
       emit_library_call (bcopy_libfunc, 0,
                         VOIDmode, 3, XEXP (from_rtx, 0), Pmode,
                         XEXP (to_rtx, 0), Pmode,
-                        convert_to_mode (TYPE_MODE (sizetype),
-                                         size, TREE_UNSIGNED (sizetype)),
-                        TYPE_MODE (sizetype));
+                        convert_to_mode (TYPE_MODE (integer_type_node),
+                                         size, TREE_UNSIGNED (integer_type_node)),
+                        TYPE_MODE (integer_type_node));
 #endif
 
       preserve_temp_slots (to_rtx);
@@ -2668,6 +2934,7 @@ store_expr (exp, target, want_value)
       emit_queue ();
       target = protect_from_queue (target, 1);
 
+      do_pending_stack_adjust ();
       NO_DEFER_POP;
       jumpifnot (TREE_OPERAND (exp, 0), lab1);
       store_expr (TREE_OPERAND (exp, 1), target, 0);
@@ -2721,11 +2988,33 @@ store_expr (exp, target, want_value)
        and then convert to the wider mode.  Our value is the computed
        expression.  */
     {
+      /* 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.  */
+      if (! want_value)
+       {
+         if (TREE_UNSIGNED (TREE_TYPE (exp))
+             != SUBREG_PROMOTED_UNSIGNED_P (target))
+           exp
+             = convert
+               (signed_or_unsigned_type (SUBREG_PROMOTED_UNSIGNED_P (target),
+                                         TREE_TYPE (exp)),
+                exp);
+
+         exp = convert (type_for_mode (GET_MODE (SUBREG_REG (target)),
+                                       SUBREG_PROMOTED_UNSIGNED_P (target)),
+                        exp);
+       }
+        
       temp = expand_expr (exp, NULL_RTX, VOIDmode, 0);
 
       /* If TEMP is a volatile MEM and we want a result value, make
-        the access now so it gets done only once.  */
-      if (GET_CODE (temp) == MEM && MEM_VOLATILE_P (temp))
+        the access now so it gets done only once.  Likewise if
+        it contains TARGET.  */
+      if (GET_CODE (temp) == MEM && want_value
+         && (MEM_VOLATILE_P (temp)
+             || reg_mentioned_p (SUBREG_REG (target), XEXP (temp, 0))))
        temp = copy_to_reg (temp);
 
       /* If TEMP is a VOIDmode constant, use convert_modes to make
@@ -2820,24 +3109,25 @@ store_expr (exp, target, want_value)
              emit_block_move (target, temp, copy_size_rtx,
                               TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT);
 
-             /* Figure out how much is left in TARGET
-                that we have to clear.  */
+             /* Figure out how much is left in TARGET that we have to clear.
+                Do all calculations in ptr_mode.  */
+
+             addr = XEXP (target, 0);
+             addr = convert_modes (ptr_mode, Pmode, addr, 1);
+
              if (GET_CODE (copy_size_rtx) == CONST_INT)
                {
-                 addr = plus_constant (XEXP (target, 0),
-                                       TREE_STRING_LENGTH (exp));
+                 addr = plus_constant (addr, TREE_STRING_LENGTH (exp));
                  size = plus_constant (size, - TREE_STRING_LENGTH (exp));
                }
              else
                {
-                 enum machine_mode size_mode = Pmode;
-
-                 addr = force_reg (Pmode, XEXP (target, 0));
-                 addr = expand_binop (size_mode, add_optab, addr,
+                 addr = force_reg (ptr_mode, addr);
+                 addr = expand_binop (ptr_mode, add_optab, addr,
                                       copy_size_rtx, NULL_RTX, 0,
                                       OPTAB_LIB_WIDEN);
 
-                 size = expand_binop (size_mode, sub_optab, size,
+                 size = expand_binop (ptr_mode, sub_optab, size,
                                       copy_size_rtx, NULL_RTX, 0,
                                       OPTAB_LIB_WIDEN);
 
@@ -2850,11 +3140,20 @@ store_expr (exp, target, want_value)
              if (size != const0_rtx)
                {
 #ifdef TARGET_MEM_FUNCTIONS
-                 emit_library_call (memset_libfunc, 0, VOIDmode, 3, addr,
-                                    Pmode, const0_rtx, Pmode, size, Pmode);
+                 emit_library_call (memset_libfunc, 0, VOIDmode, 3,
+                                    addr, Pmode,
+                                    const0_rtx, TYPE_MODE (integer_type_node),
+                                    convert_to_mode (TYPE_MODE (sizetype),
+                                                     size,
+                                                     TREE_UNSIGNED (sizetype)),
+                                    TYPE_MODE (sizetype));
 #else
                  emit_library_call (bzero_libfunc, 0, VOIDmode, 2,
-                                    addr, Pmode, size, Pmode);
+                                    addr, Pmode,
+                                    convert_to_mode (TYPE_MODE (integer_type_node),
+                                                     size,
+                                                     TREE_UNSIGNED (integer_type_node)),
+                                    TYPE_MODE (integer_type_node));
 #endif
                }
 
@@ -2888,13 +3187,116 @@ store_expr (exp, target, want_value)
     return target;
 }
 \f
+/* Return 1 if EXP just contains zeros.  */
+
+static int
+is_zeros_p (exp)
+     tree exp;
+{
+  tree elt;
+
+  switch (TREE_CODE (exp))
+    {
+    case CONVERT_EXPR:
+    case NOP_EXPR:
+    case NON_LVALUE_EXPR:
+      return is_zeros_p (TREE_OPERAND (exp, 0));
+
+    case INTEGER_CST:
+      return TREE_INT_CST_LOW (exp) == 0 && TREE_INT_CST_HIGH (exp) == 0;
+
+    case COMPLEX_CST:
+      return
+       is_zeros_p (TREE_REALPART (exp)) && is_zeros_p (TREE_IMAGPART (exp));
+
+    case REAL_CST:
+      return REAL_VALUES_EQUAL (TREE_REAL_CST (exp), dconst0);
+
+    case CONSTRUCTOR:
+      if (TREE_TYPE (exp) && TREE_CODE (TREE_TYPE (exp)) == SET_TYPE)
+       return CONSTRUCTOR_ELTS (exp) == NULL_TREE;
+      for (elt = CONSTRUCTOR_ELTS (exp); elt; elt = TREE_CHAIN (elt))
+       if (! is_zeros_p (TREE_VALUE (elt)))
+         return 0;
+
+      return 1;
+    }
+
+  return 0;
+}
+
+/* Return 1 if EXP contains mostly (3/4)  zeros.  */
+
+static int
+mostly_zeros_p (exp)
+     tree exp;
+{
+  if (TREE_CODE (exp) == CONSTRUCTOR)
+    {
+      int elts = 0, zeros = 0;
+      tree elt = CONSTRUCTOR_ELTS (exp);
+      if (TREE_TYPE (exp) && TREE_CODE (TREE_TYPE (exp)) == SET_TYPE)
+       {
+         /* If there are no ranges of true bits, it is all zero.  */
+         return elt == NULL_TREE;
+       }
+      for (; elt; elt = TREE_CHAIN (elt))
+       {
+         /* We do not handle the case where the index is a RANGE_EXPR,
+            so the statistic will be somewhat inaccurate.
+            We do make a more accurate count in store_constructor itself,
+            so since this function is only used for nested array elements,
+            this should be close enough. */
+         if (mostly_zeros_p (TREE_VALUE (elt)))
+           zeros++;
+         elts++;
+       }
+
+      return 4 * zeros >= 3 * elts;
+    }
+
+  return is_zeros_p (exp);
+}
+\f
+/* Helper function for store_constructor.
+   TARGET, BITSIZE, BITPOS, MODE, EXP are as for store_field.
+   TYPE is the type of the CONSTRUCTOR, not the element type.
+   CLEARED is as for store_constructor.  */
+
+static void
+store_constructor_field (target, bitsize, bitpos,
+                        mode, exp, type, cleared)
+     rtx target;
+     int bitsize, bitpos;
+     enum machine_mode mode;
+     tree exp, type;
+     int cleared;
+{
+  if (TREE_CODE (exp) == CONSTRUCTOR
+      && (bitpos % BITS_PER_UNIT) == 0)
+    {
+      bitpos /= BITS_PER_UNIT;
+      store_constructor (exp,
+                        change_address (target, VOIDmode,
+                                        plus_constant (XEXP (target, 0),
+                                                       bitpos)),
+                        cleared);
+    }
+  else
+    store_field (target, bitsize, bitpos, mode, exp,
+                VOIDmode, 0, TYPE_ALIGN (type) / BITS_PER_UNIT,
+                int_size_in_bytes (type));
+}
+
 /* Store the value of constructor EXP into the rtx TARGET.
-   TARGET is either a REG or a MEM.  */
+   TARGET is either a REG or a MEM.
+   CLEARED is true if TARGET is known to have been zero'd. */
 
 static void
-store_constructor (exp, target)
+store_constructor (exp, target, cleared)
      tree exp;
      rtx target;
+     int cleared;
 {
   tree type = TREE_TYPE (exp);
 
@@ -2906,7 +3308,7 @@ store_constructor (exp, target)
   if (GET_CODE (target) == REG && REGNO (target) < FIRST_PSEUDO_REGISTER)
     {
       rtx temp = gen_reg_rtx (GET_MODE (target));
-      store_constructor (exp, temp);
+      store_constructor (exp, temp, 0);
       emit_move_insn (target, temp);
       return;
     }
@@ -2924,15 +3326,30 @@ store_constructor (exp, target)
 
       /* If we are building a static constructor into a register,
         set the initial value as zero so we can fold the value into
-        a constant.  */
-      else if (GET_CODE (target) == REG && TREE_STATIC (exp))
-       emit_move_insn (target, const0_rtx);
+        a constant.  But if more than one register is involved,
+        this probably loses.  */
+      else if (GET_CODE (target) == REG && TREE_STATIC (exp)
+              && GET_MODE_SIZE (GET_MODE (target)) <= UNITS_PER_WORD)
+       {
+         if (! cleared)
+           emit_move_insn (target, const0_rtx);
 
-      /* If the constructor has fewer fields than the structure,
+         cleared = 1;
+       }
+
+      /* If the constructor has fewer fields than the structure
+        or if we are initializing the structure to mostly zeros,
         clear the whole structure first.  */
-      else if (list_length (CONSTRUCTOR_ELTS (exp))
-              != list_length (TYPE_FIELDS (type)))
-       clear_storage (target, int_size_in_bytes (type));
+      else if ((list_length (CONSTRUCTOR_ELTS (exp))
+               != list_length (TYPE_FIELDS (type)))
+              || mostly_zeros_p (exp))
+       {
+         if (! cleared)
+           clear_storage (target, expr_size (exp),
+                          TYPE_ALIGN (type) / BITS_PER_UNIT);
+
+         cleared = 1;
+       }
       else
        /* Inform later passes that the old value is dead.  */
        emit_insn (gen_rtx (CLOBBER, VOIDmode, target));
@@ -2956,6 +3373,9 @@ store_constructor (exp, target)
          if (field == 0)
            continue;
 
+         if (cleared && is_zeros_p (TREE_VALUE (elt)))
+           continue;
+
          bitsize = TREE_INT_CST_LOW (DECL_SIZE (field));
          unsignedp = TREE_UNSIGNED (field);
          mode = DECL_MODE (field);
@@ -2972,7 +3392,7 @@ store_constructor (exp, target)
            offset = pos;
 
          if (constant)
-           bitpos = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field));
+           bitpos = TREE_INT_CST_LOW (constant);
 
          if (offset)
            {
@@ -2991,34 +3411,78 @@ store_constructor (exp, target)
 
              to_rtx
                = change_address (to_rtx, VOIDmode,
-                                 gen_rtx (PLUS, Pmode, XEXP (to_rtx, 0),
-                                          force_reg (Pmode, offset_rtx)));
+                                 gen_rtx (PLUS, ptr_mode, XEXP (to_rtx, 0),
+                                          force_reg (ptr_mode, offset_rtx)));
+           }
+         if (TREE_READONLY (field))
+           {
+             if (GET_CODE (to_rtx) == MEM)
+               to_rtx = change_address (to_rtx, GET_MODE (to_rtx),
+                                        XEXP (to_rtx, 0));
+             RTX_UNCHANGING_P (to_rtx) = 1;
            }
 
-         store_field (to_rtx, bitsize, bitpos, mode, TREE_VALUE (elt),
-                      /* The alignment of TARGET is
-                         at least what its type requires.  */
-                      VOIDmode, 0,
-                      TYPE_ALIGN (type) / BITS_PER_UNIT,
-                      int_size_in_bytes (type));
+         store_constructor_field (to_rtx, bitsize, bitpos,
+                                  mode, TREE_VALUE (elt), type, cleared);
        }
     }
   else if (TREE_CODE (type) == ARRAY_TYPE)
     {
       register tree elt;
       register int i;
+      int need_to_clear;
       tree domain = TYPE_DOMAIN (type);
       HOST_WIDE_INT minelt = TREE_INT_CST_LOW (TYPE_MIN_VALUE (domain));
       HOST_WIDE_INT maxelt = TREE_INT_CST_LOW (TYPE_MAX_VALUE (domain));
       tree elttype = TREE_TYPE (type);
 
-      /* If the constructor has fewer fields than the structure,
-        clear the whole structure first.  Similarly if this this is
-        static constructor of a non-BLKmode object.  */
-
-      if (list_length (CONSTRUCTOR_ELTS (exp)) < maxelt - minelt + 1
-         || (GET_CODE (target) == REG && TREE_STATIC (exp)))
-       clear_storage (target, int_size_in_bytes (type));
+      /* If the constructor has fewer elements than the array,
+         clear the whole array first.  Similarly if this this is
+         static constructor of a non-BLKmode object.  */
+      if (cleared || (GET_CODE (target) == REG && TREE_STATIC (exp)))
+       need_to_clear = 1;
+      else
+       {
+         HOST_WIDE_INT count = 0, zero_count = 0;
+         need_to_clear = 0;
+         /* 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;
+              elt = TREE_CHAIN (elt), i++)
+           {
+             tree index = TREE_PURPOSE (elt);
+             HOST_WIDE_INT this_node_count;
+             if (index != NULL_TREE && TREE_CODE (index) == RANGE_EXPR)
+               {
+                 tree lo_index = TREE_OPERAND (index, 0);
+                 tree hi_index = TREE_OPERAND (index, 1);
+                 if (TREE_CODE (lo_index) != INTEGER_CST
+                     || TREE_CODE (hi_index) != INTEGER_CST)
+                   {
+                     need_to_clear = 1;
+                     break;
+                   }
+                 this_node_count = TREE_INT_CST_LOW (hi_index)
+                   - TREE_INT_CST_LOW (lo_index) + 1;
+               }
+             else
+               this_node_count = 1;
+             count += this_node_count;
+             if (mostly_zeros_p (TREE_VALUE (elt)))
+               zero_count += this_node_count;
+           }
+         if (4 * zero_count >= 3 * count)
+           need_to_clear = 1;
+       }
+      if (need_to_clear)
+       {
+         if (! cleared)
+           clear_storage (target, expr_size (exp),
+                          TYPE_ALIGN (type) / BITS_PER_UNIT);
+         cleared = 1;
+       }
       else
        /* Inform later passes that the old value is dead.  */
        emit_insn (gen_rtx (CLOBBER, VOIDmode, target));
@@ -3034,25 +3498,123 @@ store_constructor (exp, target)
          int bitsize;
          int bitpos;
          int unsignedp;
+         tree value = TREE_VALUE (elt);
          tree index = TREE_PURPOSE (elt);
          rtx xtarget = target;
 
+         if (cleared && is_zeros_p (value))
+           continue;
+
          mode = TYPE_MODE (elttype);
          bitsize = GET_MODE_BITSIZE (mode);
          unsignedp = TREE_UNSIGNED (elttype);
 
-         if (index != 0 && TREE_CODE (index) != INTEGER_CST)
+         if (index != NULL_TREE && TREE_CODE (index) == RANGE_EXPR)
            {
-             /* We don't currently allow variable indices in a
-                C initializer, but let's try here to support them.  */
-             rtx pos_rtx, addr, xtarget;
+             tree lo_index = TREE_OPERAND (index, 0);
+             tree hi_index = TREE_OPERAND (index, 1);
+             rtx index_r, pos_rtx, addr, hi_r, loop_top, loop_end;
+             struct nesting *loop;
+               tree position;
+
+             if (TREE_CODE (lo_index) == INTEGER_CST
+                 && TREE_CODE (hi_index) == INTEGER_CST)
+               {
+                 HOST_WIDE_INT lo = TREE_INT_CST_LOW (lo_index);
+                 HOST_WIDE_INT hi = TREE_INT_CST_LOW (hi_index);
+                 HOST_WIDE_INT count = hi - lo + 1;
+
+                 /* If the range is constant and "small", unroll the loop.
+                    We must also use store_field if the target is not MEM. */
+                 if (GET_CODE (target) != MEM
+                     || count <= 2
+                     || (TREE_CODE (TYPE_SIZE (elttype)) == INTEGER_CST
+                         && TREE_INT_CST_LOW (TYPE_SIZE (elttype)) * count
+                         <= 40 * 8))
+                   {
+                     lo -= minelt;  hi -= minelt;
+                     for (; lo <= hi; lo++)
+                       {
+                         bitpos = lo * TREE_INT_CST_LOW (TYPE_SIZE (elttype));
+                         store_constructor_field (target, bitsize, bitpos,
+                                                  mode, value, type, cleared);
+                       }
+                   }
+               }
+             else
+               {
+                 hi_r = expand_expr (hi_index, NULL_RTX, VOIDmode, 0);
+                 loop_top = gen_label_rtx ();
+                 loop_end = gen_label_rtx ();
+
+                 unsignedp = TREE_UNSIGNED (domain);
+
+                 index = build_decl (VAR_DECL, NULL_TREE, domain);
+
+                 DECL_RTL (index) = index_r
+                   = gen_reg_rtx (promote_mode (domain, DECL_MODE (index),
+                                                &unsignedp, 0));
+
+                 if (TREE_CODE (value) == SAVE_EXPR
+                     && SAVE_EXPR_RTL (value) == 0)
+                   {
+                     /* Make sure value gets expanded once before the loop. */
+                     expand_expr (value, const0_rtx, VOIDmode, 0);
+                     emit_queue ();
+                   }
+                 store_expr (lo_index, index_r, 0);
+                 loop = expand_start_loop (0);
+
+                 /* Assign value to element index. */
+                 position = size_binop (EXACT_DIV_EXPR, TYPE_SIZE (elttype),
+                                        size_int (BITS_PER_UNIT));
+                 position = size_binop (MULT_EXPR,
+                                        size_binop (MINUS_EXPR, index,
+                                                    TYPE_MIN_VALUE (domain)),
+                                        position);
+                 pos_rtx = expand_expr (position, 0, VOIDmode, 0);
+                 addr = gen_rtx (PLUS, Pmode, XEXP (target, 0), pos_rtx);
+                 xtarget = change_address (target, mode, addr);
+                 if (TREE_CODE (value) == CONSTRUCTOR)
+                   store_constructor (exp, xtarget, cleared);
+                 else
+                   store_expr (value, xtarget, 0);
+
+                 expand_exit_loop_if_false (loop,
+                                            build (LT_EXPR, integer_type_node,
+                                                   index, hi_index));
+
+                 expand_increment (build (PREINCREMENT_EXPR,
+                                          TREE_TYPE (index),
+                                          index, integer_one_node), 0);
+                 expand_end_loop ();
+                 emit_label (loop_end);
+
+                 /* Needed by stupid register allocation. to extend the
+                    lifetime of pseudo-regs used by target past the end
+                    of the loop.  */
+                 emit_insn (gen_rtx (USE, GET_MODE (target), target));
+               }
+           }
+         else if ((index != 0 && TREE_CODE (index) != INTEGER_CST)
+             || TREE_CODE (TYPE_SIZE (elttype)) != INTEGER_CST)
+           {
+             rtx pos_rtx, addr;
              tree position;
 
-             position = size_binop (MULT_EXPR, index, TYPE_SIZE (elttype));
+             if (index == 0)
+               index = size_int (i);
+
+             if (minelt)
+               index = size_binop (MINUS_EXPR, index,
+                                   TYPE_MIN_VALUE (domain));
+             position = size_binop (EXACT_DIV_EXPR, TYPE_SIZE (elttype),
+                                    size_int (BITS_PER_UNIT));
+             position = size_binop (MULT_EXPR, index, position);
              pos_rtx = expand_expr (position, 0, VOIDmode, 0);
              addr = gen_rtx (PLUS, Pmode, XEXP (target, 0), pos_rtx);
              xtarget = change_address (target, mode, addr);
-             store_expr (TREE_VALUE (elt), xtarget, 0);
+             store_expr (value, xtarget, 0);
            }
          else
            {
@@ -3061,15 +3623,188 @@ store_constructor (exp, target)
                          * TREE_INT_CST_LOW (TYPE_SIZE (elttype)));
              else
                bitpos = (i * TREE_INT_CST_LOW (TYPE_SIZE (elttype)));
+             store_constructor_field (target, bitsize, bitpos,
+                                      mode, value, type, cleared);
+           }
+       }
+    }
+  /* set constructor assignments */
+  else if (TREE_CODE (type) == SET_TYPE)
+    {
+      tree elt = CONSTRUCTOR_ELTS (exp);
+      rtx xtarget = XEXP (target, 0);
+      int set_word_size = TYPE_ALIGN (type);
+      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 (if available) or bzero.
+        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
+        bzero/memset), and set the bits we want. */
+       
+      /* Check for all zeros. */
+      if (elt == NULL_TREE)
+       {
+         if (!cleared)
+           clear_storage (target, expr_size (exp),
+                          TYPE_ALIGN (type) / BITS_PER_UNIT);
+         return;
+       }
 
-             store_field (xtarget, bitsize, bitpos, mode, TREE_VALUE (elt),
-                          /* The alignment of TARGET is
-                             at least what its type requires.  */
-                          VOIDmode, 0,
-                          TYPE_ALIGN (type) / BITS_PER_UNIT,
-                          int_size_in_bytes (type));
+      domain_min = convert (sizetype, TYPE_MIN_VALUE (domain));
+      domain_max = convert (sizetype, TYPE_MAX_VALUE (domain));
+      bitlength = size_binop (PLUS_EXPR,
+                             size_binop (MINUS_EXPR, domain_max, domain_min),
+                             size_one_node);
+
+      if (nbytes < 0 || TREE_CODE (bitlength) != INTEGER_CST)
+       abort ();
+      nbits = TREE_INT_CST_LOW (bitlength);
+
+      /* 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))
+       {
+         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 = (char*) alloca (nbits);
+         HOST_WIDE_INT word = 0;
+         int bit_pos = 0;
+         int ibit = 0;
+         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 (word);
+                     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 (GET_CODE (target) == MEM)
+                       {
+                         to_rtx = plus_constant (XEXP (target, 0), offset);
+                         to_rtx = change_address (target, mode, to_rtx);
+                       }
+                     else if (offset == 0) 
+                       to_rtx = target;
+                     else
+                       abort ();
+                     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
+                 : (TREE_CODE (TREE_VALUE (elt)) != INTEGER_CST
+                    || TREE_CODE (TREE_PURPOSE (elt)) != INTEGER_CST
+                    || (TREE_INT_CST_LOW (TREE_VALUE (elt))
+                        - TREE_INT_CST_LOW (TREE_PURPOSE (elt)) + 1
+                        != nbits))))
+           clear_storage (target, expr_size (exp),
+                          TYPE_ALIGN (type) / BITS_PER_UNIT);
+       }
+         
+      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_stack_temp (GET_MODE (target),
+                                          GET_MODE_SIZE (GET_MODE (target)),
+                                          0);
+             emit_move_insn (targetx, target);
+           }
+         else if (GET_CODE (target) == MEM)
+           targetx = target;
+         else
+           abort ();
+
+#ifdef TARGET_MEM_FUNCTIONS
+         /* 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, 0,
+                                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
+#endif
+           {
+             emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__setbits"),
+                                0, 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);
+       }
     }
 
   else
@@ -3153,9 +3888,9 @@ store_field (target, bitsize, bitpos, mode, exp, value_mode,
       || GET_CODE (target) == SUBREG
       /* If the field isn't aligned enough to store as an ordinary memref,
         store it as a bit field.  */
-      || (STRICT_ALIGNMENT
+      || (SLOW_UNALIGNED_ACCESS
          && align * BITS_PER_UNIT < GET_MODE_ALIGNMENT (mode))
-      || (STRICT_ALIGNMENT && bitpos % GET_MODE_ALIGNMENT (mode) != 0))
+      || (SLOW_UNALIGNED_ACCESS && bitpos % GET_MODE_ALIGNMENT (mode) != 0))
     {
       rtx temp = expand_expr (exp, NULL_RTX, VOIDmode, 0);
 
@@ -3327,6 +4062,7 @@ get_inner_reference (exp, pbitsize, pbitpos, poffset, pmode,
          tree pos = (TREE_CODE (exp) == COMPONENT_REF
                      ? DECL_FIELD_BITPOS (TREE_OPERAND (exp, 1))
                      : TREE_OPERAND (exp, 2));
+         tree constant = integer_zero_node, var = pos;
 
          /* If this field hasn't been filled in yet, don't go
             past it.  This should only happen when folding expressions
@@ -3334,37 +4070,20 @@ get_inner_reference (exp, pbitsize, pbitpos, poffset, pmode,
          if (pos == 0)
            break;
 
-         if (TREE_CODE (pos) == PLUS_EXPR)
-           {
-             tree constant, var;
-             if (TREE_CODE (TREE_OPERAND (pos, 0)) == INTEGER_CST)
-               {
-                 constant = TREE_OPERAND (pos, 0);
-                 var = TREE_OPERAND (pos, 1);
-               }
-             else if (TREE_CODE (TREE_OPERAND (pos, 1)) == INTEGER_CST)
-               {
-                 constant = TREE_OPERAND (pos, 1);
-                 var = TREE_OPERAND (pos, 0);
-               }
-             else
-               abort ();
-
-             *pbitpos += TREE_INT_CST_LOW (constant);
-             offset = size_binop (PLUS_EXPR, offset,
-                                  size_binop (FLOOR_DIV_EXPR, var,
-                                              size_int (BITS_PER_UNIT)));
-           }
+         /* Assume here that the offset is a multiple of a unit.
+            If not, there should be an explicitly added constant.  */
+         if (TREE_CODE (pos) == PLUS_EXPR
+             && TREE_CODE (TREE_OPERAND (pos, 1)) == INTEGER_CST)
+           constant = TREE_OPERAND (pos, 1), var = TREE_OPERAND (pos, 0);
          else if (TREE_CODE (pos) == INTEGER_CST)
-           *pbitpos += TREE_INT_CST_LOW (pos);
-         else
-           {
-             /* Assume here that the offset is a multiple of a unit.
-                If not, there should be an explicitly added constant.  */
-             offset = size_binop (PLUS_EXPR, offset,
-                                  size_binop (FLOOR_DIV_EXPR, pos,
-                                              size_int (BITS_PER_UNIT)));
-           }
+           constant = pos, var = integer_zero_node;
+
+         *pbitpos += TREE_INT_CST_LOW (constant);
+
+         if (var)
+           offset = size_binop (PLUS_EXPR, offset,
+                                size_binop (EXACT_DIV_EXPR, var,
+                                            size_int (BITS_PER_UNIT)));
        }
 
       else if (TREE_CODE (exp) == ARRAY_REF)
@@ -3382,9 +4101,10 @@ get_inner_reference (exp, pbitsize, pbitpos, poffset, pmode,
          if (! integer_zerop (low_bound))
            index = fold (build (MINUS_EXPR, index_type, index, low_bound));
 
-         if (TYPE_PRECISION (index_type) != POINTER_SIZE)
+         if (TYPE_PRECISION (index_type) != TYPE_PRECISION (sizetype))
            {
-             index = convert (type_for_size (POINTER_SIZE, 0), index);
+             index = convert (type_for_size (TYPE_PRECISION (sizetype), 0),
+                              index);
              index_type = TREE_TYPE (index);
            }
 
@@ -3402,6 +4122,9 @@ get_inner_reference (exp, pbitsize, pbitpos, poffset, pmode,
       else if (TREE_CODE (exp) != NON_LVALUE_EXPR
               && ! ((TREE_CODE (exp) == NOP_EXPR
                      || TREE_CODE (exp) == CONVERT_EXPR)
+                    && ! (TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE
+                          && (TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 0)))
+                              != UNION_TYPE))
                     && (TYPE_MODE (TREE_TYPE (exp))
                         == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))))
        break;
@@ -3530,8 +4253,7 @@ save_noncopied_parts (lhs, list)
        tree part = TREE_VALUE (tail);
        tree part_type = TREE_TYPE (part);
        tree to_be_saved = build (COMPONENT_REF, part_type, lhs, part);
-       rtx target = assign_stack_temp (TYPE_MODE (part_type),
-                                       int_size_in_bytes (part_type), 0);
+       rtx target = assign_temp (part_type, 0, 1, 1);
        if (! memory_address_p (TYPE_MODE (part_type), XEXP (target, 0)))
          target = change_address (target, TYPE_MODE (part_type), NULL_RTX);
        parts = tree_cons (to_be_saved,
@@ -3583,9 +4305,11 @@ safe_from_p (x, exp)
       /* If EXP has varying size, we MUST use a target since we currently
         have no way of allocating temporaries of variable size.  So we
         assume here that something at a higher level has prevented a
-        clash.  This is somewhat bogus, but the best we can do.  */
+        clash.  This is somewhat bogus, but the best we can do.  Only
+        do this when X is BLKmode.  */
       || (TREE_TYPE (exp) != 0 && TYPE_SIZE (TREE_TYPE (exp)) != 0
-         && TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) != INTEGER_CST))
+         && TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) != INTEGER_CST
+         && GET_MODE (x) == BLKmode))
     return 1;
 
   /* If this is a subreg of a hard register, declare it unsafe, otherwise,
@@ -3662,11 +4386,13 @@ safe_from_p (x, exp)
          break;
 
        case RTL_EXPR:
-         exp_rtl = RTL_EXPR_RTL (exp);
-         if (exp_rtl == 0)
-           /* We don't know what this can modify.  */
+         /* If a sequence exists, we would have to scan every instruction
+            in the sequence to see if it was safe.  This is probably not
+            worthwhile.  */
+         if (RTL_EXPR_SEQUENCE (exp))
            return 0;
 
+         exp_rtl = RTL_EXPR_RTL (exp);
          break;
 
        case WITH_CLEANUP_EXPR:
@@ -3935,6 +4661,9 @@ expand_expr (exp, target, tmode, modifier)
          TREE_USED (exp) = 1;
        }
 
+      /* Show we haven't gotten RTL for this yet.  */
+      temp = 0;
+
       /* Handle variables inherited from containing functions.  */
       context = decl_function_context (exp);
 
@@ -3962,32 +4691,44 @@ expand_expr (exp, target, tmode, modifier)
                            fix_lexical_addr (XEXP (addr, 0), exp));
          else
            addr = fix_lexical_addr (addr, exp);
-         return change_address (DECL_RTL (exp), mode, addr);
+         temp = change_address (DECL_RTL (exp), mode, addr);
        }
 
       /* This is the case of an array whose size is to be determined
         from its initializer, while the initializer is still being parsed.
         See expand_decl.  */
 
-      if (GET_CODE (DECL_RTL (exp)) == MEM
-         && GET_CODE (XEXP (DECL_RTL (exp), 0)) == REG)
-       return change_address (DECL_RTL (exp), GET_MODE (DECL_RTL (exp)),
+      else if (GET_CODE (DECL_RTL (exp)) == MEM
+              && GET_CODE (XEXP (DECL_RTL (exp), 0)) == REG)
+       temp = change_address (DECL_RTL (exp), GET_MODE (DECL_RTL (exp)),
                               XEXP (DECL_RTL (exp), 0));
 
       /* If DECL_RTL is memory, we are in the normal case and either
-        the address is not valid or it is not a register and -fforce-addr
-        is specified, get the address into a register.  */
-
-      if (GET_CODE (DECL_RTL (exp)) == MEM
-         && modifier != EXPAND_CONST_ADDRESS
-         && modifier != EXPAND_SUM
-         && modifier != EXPAND_INITIALIZER
-         && (! memory_address_p (DECL_MODE (exp), XEXP (DECL_RTL (exp), 0))
-             || (flag_force_addr
-                 && GET_CODE (XEXP (DECL_RTL (exp), 0)) != REG)))
-       return change_address (DECL_RTL (exp), VOIDmode,
+        the address is not valid or it is not a register and -fforce-addr
+        is specified, get the address into a register.  */
+
+      else if (GET_CODE (DECL_RTL (exp)) == MEM
+              && modifier != EXPAND_CONST_ADDRESS
+              && modifier != EXPAND_SUM
+              && modifier != EXPAND_INITIALIZER
+              && (! memory_address_p (DECL_MODE (exp),
+                                      XEXP (DECL_RTL (exp), 0))
+                  || (flag_force_addr
+                      && GET_CODE (XEXP (DECL_RTL (exp), 0)) != REG)))
+       temp = change_address (DECL_RTL (exp), VOIDmode,
                               copy_rtx (XEXP (DECL_RTL (exp), 0)));
 
+      /* If we got something, return it.  But first, set the alignment
+        the address is a register.  */
+      if (temp != 0)
+       {
+         if (GET_CODE (temp) == MEM && GET_CODE (XEXP (temp, 0)) == REG)
+           mark_reg_pointer (XEXP (temp, 0),
+                             DECL_ALIGN (exp) / BITS_PER_UNIT);
+
+         return temp;
+       }
+
       /* If the mode of DECL_RTL does not match that of the decl, it
         must be a promoted value.  We return a SUBREG of the wanted mode,
         but mark it so that we know that it was already extended.  */
@@ -4075,14 +4816,10 @@ expand_expr (exp, target, tmode, modifier)
        }
       if (SAVE_EXPR_RTL (exp) == 0)
        {
-         if (mode == BLKmode)
-           {
-             temp
-               = assign_stack_temp (mode, int_size_in_bytes (type), 0);
-             MEM_IN_STRUCT_P (temp) = AGGREGATE_TYPE_P (type);
-           }
+         if (mode == VOIDmode)
+           temp = const0_rtx;
          else
-           temp = gen_reg_rtx (promote_mode (type, mode, &unsignedp, 0));
+           temp = assign_temp (type, 0, 0, 0);
 
          SAVE_EXPR_RTL (exp) = temp;
          if (!optimize && GET_CODE (temp) == REG)
@@ -4102,7 +4839,10 @@ expand_expr (exp, target, tmode, modifier)
              SUBREG_PROMOTED_UNSIGNED_P (temp) = unsignedp;
            }
 
-         store_expr (TREE_OPERAND (exp, 0), temp, 0);
+         if (temp == const0_rtx)
+           expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, 0);
+         else
+           store_expr (TREE_OPERAND (exp, 0), temp, 0);
        }
 
       /* If the mode of SAVE_EXPR_RTL does not match that of the expression, it
@@ -4132,7 +4872,8 @@ expand_expr (exp, target, tmode, modifier)
          tree old_list = placeholder_list;
 
          for (object = TREE_PURPOSE (placeholder_list);
-              TREE_TYPE (object) != type
+              (TYPE_MAIN_VARIANT (TREE_TYPE (object))
+               != TYPE_MAIN_VARIANT (type))
               && (TREE_CODE_CLASS (TREE_CODE (object)) == 'r'
                   || TREE_CODE_CLASS (TREE_CODE (object)) == '1'
                   || TREE_CODE_CLASS (TREE_CODE (object)) == '2'
@@ -4140,7 +4881,9 @@ expand_expr (exp, target, tmode, modifier)
               object = TREE_OPERAND (object, 0))
            ;
 
-         if (object && TREE_TYPE (object) == type)
+         if (object != 0
+             && (TYPE_MAIN_VARIANT (TREE_TYPE (object))
+                 == TYPE_MAIN_VARIANT (type)))
            {
              /* Expand this object skipping the list entries before
                 it was found in case it is also a PLACEHOLDER_EXPR.
@@ -4246,9 +4989,10 @@ expand_expr (exp, target, tmode, modifier)
                    || TREE_ADDRESSABLE (exp)
                    || (TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
                        && (move_by_pieces_ninsns
-                           (TREE_INT_CST_LOW (TYPE_SIZE (type)),
-                            TYPE_ALIGN (type))
-                           > MOVE_RATIO))))
+                           (TREE_INT_CST_LOW (TYPE_SIZE (type))/BITS_PER_UNIT,
+                            TYPE_ALIGN (type) / BITS_PER_UNIT)
+                           > MOVE_RATIO)
+                       && ! mostly_zeros_p (exp))))
               || (modifier == EXPAND_INITIALIZER && TREE_CONSTANT (exp)))
        {
          rtx constructor = output_constant_def (exp);
@@ -4271,14 +5015,18 @@ expand_expr (exp, target, tmode, modifier)
              if (mode != BLKmode && ! TREE_ADDRESSABLE (exp))
                target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode);
              else
-               {
-                 target
-                   = assign_stack_temp (mode, int_size_in_bytes (type), 0);
-                 if (AGGREGATE_TYPE_P (type))
-                   MEM_IN_STRUCT_P (target) = 1;
-               }
+               target = assign_temp (type, 0, 1, 1);
+           }
+
+         if (TREE_READONLY (exp))
+           {
+             if (GET_CODE (target) == MEM)
+               target = change_address (target, GET_MODE (target),
+                                        XEXP (target, 0));
+             RTX_UNCHANGING_P (target) = 1;
            }
-         store_constructor (exp, target);
+
+         store_constructor (exp, target, 0);
          return target;
        }
 
@@ -4295,9 +5043,7 @@ expand_expr (exp, target, tmode, modifier)
           code, suitable for indexing, may be generated.  */
        if (TREE_CODE (exp1) == SAVE_EXPR
            && SAVE_EXPR_RTL (exp1) == 0
-           && TREE_CODE (exp2 = TREE_OPERAND (exp1, 0)) != ERROR_MARK
-           && TYPE_MODE (TREE_TYPE (exp1)) == Pmode
-           && TYPE_MODE (TREE_TYPE (exp2)) == Pmode)
+           && TYPE_MODE (TREE_TYPE (exp1)) == ptr_mode)
          {
            temp = expand_expr (TREE_OPERAND (exp1, 0), NULL_RTX,
                                VOIDmode, EXPAND_SUM);
@@ -4323,11 +5069,13 @@ expand_expr (exp, target, tmode, modifier)
                && AGGREGATE_TYPE_P (TREE_TYPE (exp2))))
          MEM_IN_STRUCT_P (temp) = 1;
        MEM_VOLATILE_P (temp) = TREE_THIS_VOLATILE (exp) | flag_volatile;
-#if 0 /* It is incorrect to set RTX_UNCHANGING_P here, because the fact that
-        a location is accessed through a pointer to const does not mean
-        that the value there can never change.  */
-       RTX_UNCHANGING_P (temp) = TREE_READONLY (exp);
-#endif
+
+       /* It is incorrect to set RTX_UNCHANGING_P from TREE_READONLY
+          here, because, in C and C++, the fact that a location is accessed
+          through a pointer to const does not mean that the value there can
+          never change.  Languages where it can never change should
+          also set TREE_STATIC.  */
+       RTX_UNCHANGING_P (temp) = TREE_READONLY (exp) & TREE_STATIC (exp);
        return temp;
       }
 
@@ -4364,7 +5112,7 @@ expand_expr (exp, target, tmode, modifier)
 
        if ((TREE_CODE (index) != INTEGER_CST
             || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
-           && (! STRICT_ALIGNMENT || ! get_inner_unaligned_p (exp)))
+           && (! SLOW_UNALIGNED_ACCESS || ! get_inner_unaligned_p (exp)))
          {
            /* Nonconstant array index or nonconstant element size, and
               not an array in an unaligned (packed) structure field.
@@ -4384,10 +5132,11 @@ expand_expr (exp, target, tmode, modifier)
            tree elt;
            tree size = size_in_bytes (type);
 
-           /* Convert the integer argument to a type the same size as a
-              pointer so the multiply won't overflow spuriously.  */
-           if (TYPE_PRECISION (index_type) != POINTER_SIZE)
-             index = convert (type_for_size (POINTER_SIZE, 0), index);
+           /* Convert the integer argument to a type the same size as sizetype
+              so the multiply won't overflow spuriously.  */
+           if (TYPE_PRECISION (index_type) != TYPE_PRECISION (sizetype))
+             index = convert (type_for_size (TYPE_PRECISION (sizetype), 0),
+                              index);
 
            if (TREE_CODE (size) != INTEGER_CST
                && contains_placeholder_p (size))
@@ -4400,13 +5149,20 @@ expand_expr (exp, target, tmode, modifier)
               matter, since expand_expr should not care.)  */
            TREE_SIDE_EFFECTS (array_adr) = 0;
 
-           elt = build1 (INDIRECT_REF, type,
-                         fold (build (PLUS_EXPR,
-                                      TYPE_POINTER_TO (variant_type),
-                                      array_adr,
-                                      fold (build (MULT_EXPR,
-                                                   TYPE_POINTER_TO (variant_type),
-                                                   index, size)))));
+           elt
+             = build1
+               (INDIRECT_REF, type,
+                fold (build (PLUS_EXPR,
+                             TYPE_POINTER_TO (variant_type),
+                             array_adr,
+                             fold
+                             (build1
+                              (NOP_EXPR,
+                               TYPE_POINTER_TO (variant_type),
+                               fold (build (MULT_EXPR, TREE_TYPE (index),
+                                            index,
+                                            convert (TREE_TYPE (index),
+                                                     size))))))));;
 
            /* Volatility, etc., of new expression is same as old
               expression.  */
@@ -4485,9 +5241,12 @@ expand_expr (exp, target, tmode, modifier)
     case COMPONENT_REF:
     case BIT_FIELD_REF:
       /* If the operand is a CONSTRUCTOR, we can just extract the
-        appropriate field if it is present.  */
+        appropriate field if it is present.  Don't do this if we have
+        already written the data since we want to refer to that copy
+        and varasm.c assumes that's what we'll do.  */
       if (code != ARRAY_REF
-         && TREE_CODE (TREE_OPERAND (exp, 0)) == CONSTRUCTOR)
+         && TREE_CODE (TREE_OPERAND (exp, 0)) == CONSTRUCTOR
+         && TREE_CST_RTL (TREE_OPERAND (exp, 0)) == 0)
        {
          tree elt;
 
@@ -4513,10 +5272,17 @@ expand_expr (exp, target, tmode, modifier)
        if (tem == exp)
          abort ();
 
-       /* In some cases, we will be offsetting OP0's address by a constant.
-          So get it as a sum, if possible.  If we will be using it
-          directly in an insn, we validate it.  */
-       op0 = expand_expr (tem, NULL_RTX, VOIDmode, EXPAND_SUM);
+       /* If TEM's type is a union of variable size, pass TARGET to the inner
+          computation, since it will need a temporary and TARGET is known
+          to have to do.  This occurs in unchecked conversion in Ada.  */
+  
+       op0 = expand_expr (tem,
+                          (TREE_CODE (TREE_TYPE (tem)) == UNION_TYPE
+                           && (TREE_CODE (TYPE_SIZE (TREE_TYPE (tem)))
+                               != INTEGER_CST)
+                           ? target : NULL_RTX),
+                          VOIDmode,
+                          modifier == EXPAND_INITIALIZER ? modifier : 0);
 
        /* If this is a constant, put it into a register if it is a
           legitimate constant and memory if it isn't.  */
@@ -4537,8 +5303,8 @@ expand_expr (exp, target, tmode, modifier)
            if (GET_CODE (op0) != MEM)
              abort ();
            op0 = change_address (op0, VOIDmode,
-                                 gen_rtx (PLUS, Pmode, XEXP (op0, 0),
-                                          force_reg (Pmode, offset_rtx)));
+                                 gen_rtx (PLUS, ptr_mode, XEXP (op0, 0),
+                                          force_reg (ptr_mode, offset_rtx)));
          /* If we have a variable offset, the known alignment
             is only that of the innermost structure containing the field.
             (Actually, we could sometimes do better by using the
@@ -4563,15 +5329,16 @@ expand_expr (exp, target, tmode, modifier)
           (which we know to be the width of a basic mode), then
           storing into memory, and changing the mode to BLKmode.  */
        if (mode1 == VOIDmode
-           || (mode1 != BLKmode && ! direct_load[(int) mode1]
-               && modifier != EXPAND_CONST_ADDRESS
-               && modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
            || GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG
-           /* If the field isn't aligned enough to fetch as a memref,
-              fetch it as a bit field.  */
-           || (STRICT_ALIGNMENT
-               && TYPE_ALIGN (TREE_TYPE (tem)) < GET_MODE_ALIGNMENT (mode))
-           || (STRICT_ALIGNMENT && bitpos % GET_MODE_ALIGNMENT (mode) != 0))
+           || (modifier != EXPAND_CONST_ADDRESS
+               && modifier != EXPAND_SUM
+               && modifier != EXPAND_INITIALIZER
+               && ((mode1 != BLKmode && ! direct_load[(int) mode1])
+                   /* If the field isn't aligned enough to fetch as a memref,
+                      fetch it as a bit field.  */
+                   || (SLOW_UNALIGNED_ACCESS
+                       && ((TYPE_ALIGN (TREE_TYPE (tem)) < GET_MODE_ALIGNMENT (mode))
+                           || (bitpos % GET_MODE_ALIGNMENT (mode) != 0))))))
          {
            enum machine_mode ext_mode = mode;
 
@@ -4581,7 +5348,12 @@ expand_expr (exp, target, tmode, modifier)
            if (ext_mode == BLKmode)
              abort ();
 
-           op0 = extract_bit_field (validize_mem (op0), bitsize, bitpos,
+           op0 = validize_mem (op0);
+
+           if (GET_CODE (op0) == MEM && GET_CODE (XEXP (op0, 0)) == REG)
+             mark_reg_pointer (XEXP (op0, 0), alignment);
+
+           op0 = extract_bit_field (op0, bitsize, bitpos,
                                     unsignedp, target, ext_mode, ext_mode,
                                     alignment,
                                     int_size_in_bytes (TREE_TYPE (tem)));
@@ -4599,6 +5371,11 @@ expand_expr (exp, target, tmode, modifier)
            return op0;
          }
 
+       /* If the result is BLKmode, use that to access the object
+          now as well.  */
+       if (mode == BLKmode)
+         mode1 = BLKmode;
+
        /* Get a reference to just this component.  */
        if (modifier == EXPAND_CONST_ADDRESS
            || modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER)
@@ -4608,6 +5385,9 @@ expand_expr (exp, target, tmode, modifier)
          op0 = change_address (op0, mode1,
                                plus_constant (XEXP (op0, 0),
                                               (bitpos / BITS_PER_UNIT)));
+       if (GET_CODE (XEXP (op0, 0)) == REG)
+         mark_reg_pointer (XEXP (op0, 0), alignment);
+
        MEM_IN_STRUCT_P (op0) = 1;
        MEM_VOLATILE_P (op0) |= volatilep;
        if (mode == mode1 || mode1 == BLKmode || mode1 == tmode)
@@ -4769,7 +5549,10 @@ expand_expr (exp, target, tmode, modifier)
        int old_temp_level = target_temp_slot_level;
        push_temp_slots ();
        target_temp_slot_level = temp_slot_level;
-       op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode, modifier);
+       op0 = expand_expr (TREE_OPERAND (exp, 0), target, tmode, modifier);
+       /* If we're going to use this value, load it up now.  */
+       if (! ignore)
+         op0 = force_not_mem (op0);
        expand_cleanups_to (old_cleanups);
        preserve_temp_slots (op0);
        free_temp_slots ();
@@ -4797,38 +5580,15 @@ expand_expr (exp, target, tmode, modifier)
     case NOP_EXPR:
     case CONVERT_EXPR:
     case REFERENCE_EXPR:
-      if (mode == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))
-       {
-         op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode,
-                            modifier);
-
-         /* If the signedness of the conversion differs and OP0 is
-            a promoted SUBREG, clear that indication since we now
-            have to do the proper extension.  */
-         if (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))) != unsignedp
-             && GET_CODE (op0) == SUBREG)
-           SUBREG_PROMOTED_VAR_P (op0) = 0;
-
-         return op0;
-       }
-
       if (TREE_CODE (type) == UNION_TYPE)
        {
          tree valtype = TREE_TYPE (TREE_OPERAND (exp, 0));
          if (target == 0)
            {
-             if (mode == BLKmode)
-               {
-                 if (TYPE_SIZE (type) == 0
-                     || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
-                   abort ();
-                 target = assign_stack_temp (BLKmode,
-                                             (TREE_INT_CST_LOW (TYPE_SIZE (type))
-                                              + BITS_PER_UNIT - 1)
-                                             / BITS_PER_UNIT, 0);
-               }
-             else
+             if (mode != BLKmode)
                target = gen_reg_rtx (tmode != VOIDmode ? tmode : mode);
+             else
+               target = assign_temp (type, 0, 1, 1);
            }
 
          if (GET_CODE (target) == MEM)
@@ -4849,6 +5609,21 @@ expand_expr (exp, target, tmode, modifier)
          return target;
        }
 
+      if (mode == TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))
+       {
+         op0 = expand_expr (TREE_OPERAND (exp, 0), target, VOIDmode,
+                            modifier);
+
+         /* If the signedness of the conversion differs and OP0 is
+            a promoted SUBREG, clear that indication since we now
+            have to do the proper extension.  */
+         if (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (exp, 0))) != unsignedp
+             && GET_CODE (op0) == SUBREG)
+           SUBREG_PROMOTED_VAR_P (op0) = 0;
+
+         return op0;
+       }
+
       op0 = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, mode, 0);
       if (GET_MODE (op0) == mode)
        return op0;
@@ -4862,9 +5637,6 @@ expand_expr (exp, target, tmode, modifier)
       if (modifier == EXPAND_INITIALIZER)
        return gen_rtx (unsignedp ? ZERO_EXTEND : SIGN_EXTEND, mode, op0);
 
-      if (flag_force_mem && GET_CODE (op0) == MEM)
-       op0 = copy_to_reg (op0);
-
       if (target == 0)
        return
          convert_to_mode (mode, op0,
@@ -4903,7 +5675,7 @@ expand_expr (exp, target, tmode, modifier)
          TREE_OPERAND (TREE_OPERAND (exp, 0), 0) = t;
        }
 
-      /* If the result is to be Pmode and we are adding an integer to
+      /* If the result is to be ptr_mode and we are adding an integer to
         something, we might be forming a constant.  So try to use
         plus_constant.  If it produces a sum and we can't accept it,
         use force_operand.  This allows P = &ARR[const] to generate
@@ -4912,7 +5684,7 @@ expand_expr (exp, target, tmode, modifier)
 
         If this is an EXPAND_SUM call, always return the sum.  */
       if (modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER
-         || mode == Pmode)
+         || mode == ptr_mode)
        {
          if (TREE_CODE (TREE_OPERAND (exp, 0)) == INTEGER_CST
              && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
@@ -4954,7 +5726,7 @@ expand_expr (exp, target, tmode, modifier)
         And force_operand won't know whether to sign-extend or
         zero-extend.  */
       if ((modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
-         || mode != Pmode)
+         || mode != ptr_mode)
        goto binop;
 
       preexpand_calls (exp);
@@ -5026,10 +5798,6 @@ expand_expr (exp, target, tmode, modifier)
          rtx op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX,
                                 VOIDmode, modifier);
 
-         /* If one operand is a CONST_INT, put it last.  */
-         if (GET_CODE (op0) == CONST_INT)
-           temp = op0, op0 = op1, op1 = temp;
-
          /* If the last operand is a CONST_INT, use plus_constant of
             the negated constant.  Else make the MINUS.  */
          if (GET_CODE (op1) == CONST_INT)
@@ -5040,10 +5808,29 @@ expand_expr (exp, target, tmode, modifier)
       /* Convert A - const to A + (-const).  */
       if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST)
        {
-         exp = build (PLUS_EXPR, type, TREE_OPERAND (exp, 0),
-                      fold (build1 (NEGATE_EXPR, type,
-                                    TREE_OPERAND (exp, 1))));
-         goto plus_expr;
+         tree negated = fold (build1 (NEGATE_EXPR, type,
+                                      TREE_OPERAND (exp, 1)));
+
+         /* Deal with the case where we can't negate the constant
+            in TYPE.  */
+         if (TREE_UNSIGNED (type) || TREE_OVERFLOW (negated))
+           {
+             tree newtype = signed_type (type);
+             tree newop0 = convert (newtype, TREE_OPERAND (exp, 0));
+             tree newop1 = convert (newtype, TREE_OPERAND (exp, 1));
+             tree newneg = fold (build1 (NEGATE_EXPR, newtype, newop1));
+
+             if (! TREE_OVERFLOW (newneg))
+               return expand_expr (convert (type, 
+                                            build (PLUS_EXPR, newtype,
+                                                   newop0, newneg)),
+                                   target, tmode, modifier);
+           }
+         else
+           {
+             exp = build (PLUS_EXPR, type, TREE_OPERAND (exp, 0), negated);
+             goto plus_expr;
+           }
        }
       this_optab = sub_optab;
       goto binop;
@@ -5063,7 +5850,7 @@ expand_expr (exp, target, tmode, modifier)
       /* Attempt to return something suitable for generating an
         indexed address, for machines that support that.  */
 
-      if (modifier == EXPAND_SUM && mode == Pmode
+      if (modifier == EXPAND_SUM && mode == ptr_mode
          && TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST
          && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
        {
@@ -5118,20 +5905,46 @@ expand_expr (exp, target, tmode, modifier)
        {
          enum machine_mode innermode
            = TYPE_MODE (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)));
+         optab other_optab = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
+                       ? smul_widen_optab : umul_widen_optab);
          this_optab = (TREE_UNSIGNED (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)))
                        ? umul_widen_optab : smul_widen_optab);
-         if (mode == GET_MODE_WIDER_MODE (innermode)
-             && this_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+         if (mode == GET_MODE_WIDER_MODE (innermode))
            {
-             op0 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
-                                NULL_RTX, VOIDmode, 0);
-             if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST)
-               op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX,
-                                  VOIDmode, 0);
-             else
-               op1 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 1), 0),
-                                  NULL_RTX, VOIDmode, 0);
-             goto binop2;
+             if (this_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+               {
+                 op0 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
+                                    NULL_RTX, VOIDmode, 0);
+                 if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST)
+                   op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX,
+                                      VOIDmode, 0);
+                 else
+                   op1 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 1), 0),
+                                      NULL_RTX, VOIDmode, 0);
+                 goto binop2;
+               }
+             else if (other_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing
+                      && innermode == word_mode)
+               {
+                 rtx htem;
+                 op0 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
+                                    NULL_RTX, VOIDmode, 0);
+                 if (TREE_CODE (TREE_OPERAND (exp, 1)) == INTEGER_CST)
+                   op1 = expand_expr (TREE_OPERAND (exp, 1), NULL_RTX,
+                                      VOIDmode, 0);
+                 else
+                   op1 = expand_expr (TREE_OPERAND (TREE_OPERAND (exp, 1), 0),
+                                      NULL_RTX, VOIDmode, 0);
+                 temp = expand_binop (mode, other_optab, op0, op1, target,
+                                      unsignedp, OPTAB_LIB_WIDEN);
+                 htem = expand_mult_highpart_adjust (innermode,
+                                                     gen_highpart (innermode, temp),
+                                                     op0, op1,
+                                                     gen_highpart (innermode, temp),
+                                                     unsignedp);
+                 emit_move_insn (gen_highpart (innermode, temp), htem);
+                 return temp;
+               }
            }
        }
       op0 = expand_expr (TREE_OPERAND (exp, 0), subtarget, VOIDmode, 0);
@@ -5213,70 +6026,8 @@ expand_expr (exp, target, tmode, modifier)
       if (TREE_UNSIGNED (type))
        return op0;
 
-      /* First try to do it with a special abs instruction.  */
-      temp = expand_unop (mode, abs_optab, op0, target, 0);
-      if (temp != 0)
-       return temp;
-
-      /* If this machine has expensive jumps, we can do integer absolute
-        value of X as (((signed) x >> (W-1)) ^ x) - ((signed) x >> (W-1)),
-        where W is the width of MODE.  */
-
-      if (GET_MODE_CLASS (mode) == MODE_INT && BRANCH_COST >= 2)
-       {
-         rtx extended = expand_shift (RSHIFT_EXPR, mode, op0,
-                                      size_int (GET_MODE_BITSIZE (mode) - 1),
-                                      NULL_RTX, 0);
-
-         temp = expand_binop (mode, xor_optab, extended, op0, target, 0,
-                              OPTAB_LIB_WIDEN);
-         if (temp != 0)
-           temp = expand_binop (mode, sub_optab, temp, extended, target, 0,
-                                OPTAB_LIB_WIDEN);
-
-         if (temp != 0)
-           return temp;
-       }
-
-      /* If that does not win, use conditional jump and negate.  */
-      target = original_target;
-      op1 = gen_label_rtx ();
-      if (target == 0 || ! safe_from_p (target, TREE_OPERAND (exp, 0))
-         || GET_MODE (target) != mode
-         || (GET_CODE (target) == MEM && MEM_VOLATILE_P (target))
-         || (GET_CODE (target) == REG
-             && REGNO (target) < FIRST_PSEUDO_REGISTER))
-       target = gen_reg_rtx (mode);
-
-      emit_move_insn (target, op0);
-      NO_DEFER_POP;
-
-      /* If this mode is an integer too wide to compare properly,
-        compare word by word.  Rely on CSE to optimize constant cases.  */
-      if (GET_MODE_CLASS (mode) == MODE_INT && ! can_compare_p (mode))
-       do_jump_by_parts_greater_rtx (mode, 0, target, const0_rtx, 
-                                     NULL_RTX, op1);
-      else
-       {
-         temp = compare_from_rtx (target, CONST0_RTX (mode), GE, 0, mode,
-                                  NULL_RTX, 0);
-         if (temp == const1_rtx)
-           return target;
-         else if (temp != const0_rtx)
-           {
-             if (bcc_gen_fctn[(int) GET_CODE (temp)] != 0)
-               emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (temp)]) (op1));
-             else
-               abort ();
-           }
-       }
-
-      op0 = expand_unop (mode, neg_optab, target, target, 0);
-      if (op0 != target)
-       emit_move_insn (target, op0);
-      emit_label (op1);
-      OK_DEFER_POP;
-      return target;
+      return expand_abs (mode, op0, target, unsignedp,
+                        safe_from_p (target, TREE_OPERAND (exp, 0)));
 
     case MAX_EXPR:
     case MIN_EXPR:
@@ -5302,6 +6053,12 @@ expand_expr (exp, target, tmode, modifier)
       if (temp != 0)
        return temp;
 
+      /* At this point, a MEM target is no longer useful; we will get better
+        code without it.  */
+        
+      if (GET_CODE (target) == MEM)
+       target = gen_reg_rtx (mode);
+
       if (target != op0)
        emit_move_insn (target, op0);
 
@@ -5531,22 +6288,12 @@ expand_expr (exp, target, tmode, modifier)
          temp = 0;
        else if (original_target
                 && safe_from_p (original_target, TREE_OPERAND (exp, 0))
-                && GET_MODE (original_target) == mode)
+                && GET_MODE (original_target) == mode
+                && ! (GET_CODE (original_target) == MEM
+                      && MEM_VOLATILE_P (original_target)))
          temp = original_target;
-       else if (mode == BLKmode)
-         {
-           if (TYPE_SIZE (type) == 0
-               || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
-             abort ();
-
-           temp = assign_stack_temp (BLKmode,
-                                     (TREE_INT_CST_LOW (TYPE_SIZE (type))
-                                      + BITS_PER_UNIT - 1)
-                                     / BITS_PER_UNIT, 0);
-           MEM_IN_STRUCT_P (temp) = AGGREGATE_TYPE_P (type);
-         }
        else
-         temp = gen_reg_rtx (mode);
+         temp = assign_temp (type, 0, 0, 1);
 
        /* Check for X ? A + B : A.  If we have this, we can copy
           A to the output and conditionally add B.  Similarly for unary
@@ -5579,8 +6326,7 @@ expand_expr (exp, target, tmode, modifier)
            && (TREE_CODE (binary_op) == PLUS_EXPR
                || TREE_CODE (binary_op) == MINUS_EXPR
                || TREE_CODE (binary_op) == BIT_IOR_EXPR
-               || TREE_CODE (binary_op) == BIT_XOR_EXPR
-               || TREE_CODE (binary_op) == BIT_AND_EXPR)
+               || TREE_CODE (binary_op) == BIT_XOR_EXPR)
            && integer_onep (TREE_OPERAND (binary_op, 1))
            && TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (exp, 0))) == '<')
          {
@@ -5588,8 +6334,7 @@ expand_expr (exp, target, tmode, modifier)
            optab boptab = (TREE_CODE (binary_op) == PLUS_EXPR ? add_optab
                            : TREE_CODE (binary_op) == MINUS_EXPR ? sub_optab
                            : TREE_CODE (binary_op) == BIT_IOR_EXPR ? ior_optab
-                           : TREE_CODE (binary_op) == BIT_XOR_EXPR ? xor_optab
-                           : and_optab);
+                           : xor_optab);
 
            /* If we had X ? A : A + 1, do this as A + (X == 0).
 
@@ -5618,6 +6363,7 @@ expand_expr (exp, target, tmode, modifier)
                = invert_truthvalue (TREE_OPERAND (exp, 0));
          }
            
+       do_pending_stack_adjust ();
        NO_DEFER_POP;
        op0 = gen_label_rtx ();
 
@@ -5803,11 +6549,16 @@ expand_expr (exp, target, tmode, modifier)
            /* ??? deprecated, use sequences instead.  */
            reorder_insns (NEXT_INSN (last), get_last_insn (), dest_right_flag);
 
+           /* All cleanups must be on the function_obstack.  */
+           push_obstacks_nochange ();
+           resume_temporary_allocation ();
+
            /* convert flag, which is an rtx, into a tree. */
            cond = make_node (RTL_EXPR);
            TREE_TYPE (cond) = integer_type_node;
            RTL_EXPR_RTL (cond) = flag;
            RTL_EXPR_SEQUENCE (cond) = NULL_RTX;
+           cond = save_expr (cond);
 
            if (! left_cleanups)
              left_cleanups = integer_zero_node;
@@ -5818,6 +6569,8 @@ expand_expr (exp, target, tmode, modifier)
                                  left_cleanups, right_cleanups);
            new_cleanups = fold (new_cleanups);
 
+           pop_obstacks ();
+
            /* Now add in the conditionalized cleanups. */
            cleanups_this_call
              = tree_cons (NULL_TREE, new_cleanups, cleanups_this_call);
@@ -5845,6 +6598,9 @@ expand_expr (exp, target, tmode, modifier)
        if (TREE_CODE (slot) != VAR_DECL)
          abort ();
 
+       if (! ignore)
+         target = original_target;
+
        if (target == 0)
          {
            if (DECL_RTL (slot) != 0)
@@ -5857,7 +6613,7 @@ expand_expr (exp, target, tmode, modifier)
              }
            else
              {
-               target = assign_stack_temp (mode, int_size_in_bytes (type), 2);
+               target = assign_temp (type, 2, 1, 1);
                /* All temp slots at this level must not conflict.  */
                preserve_temp_slots (target);
                DECL_RTL (slot) = target;
@@ -6042,6 +6798,8 @@ expand_expr (exp, target, tmode, modifier)
          if (ignore)
            return op0;
 
+         op0 = protect_from_queue (op0, 0);
+
          /* We would like the object in memory.  If it is a constant,
             we can have it be statically allocated into memory.  For
             a non-constant (REG, SUBREG or CONCAT), we need to allocate some
@@ -6062,11 +6820,9 @@ expand_expr (exp, target, tmode, modifier)
              /* If this object is in a register, it must be not
                 be BLKmode. */
              tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
-             enum machine_mode inner_mode = TYPE_MODE (inner_type);
-             rtx memloc
-               = assign_stack_temp (inner_mode,
-                                    int_size_in_bytes (inner_type), 1);
+             rtx memloc = assign_temp (inner_type, 1, 1, 1);
 
+             mark_temp_addr_taken (memloc);
              emit_move_insn (memloc, op0);
              op0 = memloc;
            }
@@ -6075,7 +6831,15 @@ expand_expr (exp, target, tmode, modifier)
            abort ();
   
          if (modifier == EXPAND_SUM || modifier == EXPAND_INITIALIZER)
-           return XEXP (op0, 0);
+           {
+             temp = XEXP (op0, 0);
+#ifdef POINTERS_EXTEND_UNSIGNED
+             if (GET_MODE (temp) == Pmode && GET_MODE (temp) != mode
+                 && mode == ptr_mode)
+               temp = convert_memory_address (ptr_mode, temp);
+#endif
+             return temp;
+           }
 
          op0 = force_operand (XEXP (op0, 0), target);
        }
@@ -6083,14 +6847,21 @@ expand_expr (exp, target, tmode, modifier)
       if (flag_force_addr && GET_CODE (op0) != REG)
        op0 = force_reg (Pmode, op0);
 
-      if (GET_CODE (op0) == REG)
-       mark_reg_pointer (op0);
+      if (GET_CODE (op0) == REG
+         && ! REG_USERVAR_P (op0))
+       mark_reg_pointer (op0, TYPE_ALIGN (TREE_TYPE (type)) / BITS_PER_UNIT);
 
       /* If we might have had a temp slot, add an equivalent address
         for it.  */
       if (temp != 0)
        update_temp_slot_address (temp, op0);
 
+#ifdef POINTERS_EXTEND_UNSIGNED
+      if (GET_MODE (op0) == Pmode && GET_MODE (op0) != mode
+         && mode == ptr_mode)
+       op0 = convert_memory_address (ptr_mode, op0);
+#endif
+
       return op0;
 
     case ENTRY_VALUE_EXPR:
@@ -6888,6 +7659,58 @@ c_strlen (src)
      calculation is needed.  */
   return size_int (strlen (ptr + offset));
 }
+
+rtx
+expand_builtin_return_addr (fndecl_code, count, tem)
+     enum built_in_function fndecl_code;
+     rtx tem;
+     int count;
+{
+  int i;
+
+  /* Some machines need special handling before we can access
+     arbitrary frames.  For example, on the sparc, we must first flush
+     all register windows to the stack.  */
+#ifdef SETUP_FRAME_ADDRESSES
+  SETUP_FRAME_ADDRESSES ();
+#endif
+
+  /* On the sparc, the return address is not in the frame, it is in a
+     register.  There is no way to access it off of the current frame
+     pointer, but it can be accessed off the previous frame pointer by
+     reading the value from the register window save area.  */
+#ifdef RETURN_ADDR_IN_PREVIOUS_FRAME
+  if (fndecl_code == BUILT_IN_RETURN_ADDRESS)
+    count--;
+#endif
+
+  /* Scan back COUNT frames to the specified frame.  */
+  for (i = 0; i < count; i++)
+    {
+      /* Assume the dynamic chain pointer is in the word that the
+        frame address points to, unless otherwise specified.  */
+#ifdef DYNAMIC_CHAIN_ADDRESS
+      tem = DYNAMIC_CHAIN_ADDRESS (tem);
+#endif
+      tem = memory_address (Pmode, tem);
+      tem = copy_to_reg (gen_rtx (MEM, Pmode, tem));
+    }
+
+  /* For __builtin_frame_address, return what we've got.  */
+  if (fndecl_code == BUILT_IN_FRAME_ADDRESS)
+    return tem;
+
+  /* For __builtin_return_address, Get the return address from that
+     frame.  */
+#ifdef RETURN_ADDR_RTX
+  tem = RETURN_ADDR_RTX (count, tem);
+#else
+  tem = memory_address (Pmode,
+                       plus_constant (tem, GET_MODE_SIZE (Pmode)));
+  tem = gen_rtx (MEM, Pmode, tem);
+#endif
+  return tem;
+}
 \f
 /* Expand an expression EXP that calls a built-in function,
    with result going to TARGET if that's convenient
@@ -7224,15 +8047,18 @@ expand_builtin (exp, target, subtarget, mode, ignore)
            tree arg = TREE_VALUE (arglist);
 
            /* Strip off all nops for the sake of the comparison.  This
-              is not quite the same as STRIP_NOPS.  It does more.  */
+              is not quite the same as STRIP_NOPS.  It does more.  
+              We must also strip off INDIRECT_EXPR for C++ reference
+              parameters.  */
            while (TREE_CODE (arg) == NOP_EXPR
                   || TREE_CODE (arg) == CONVERT_EXPR
-                  || TREE_CODE (arg) == NON_LVALUE_EXPR)
+                  || TREE_CODE (arg) == NON_LVALUE_EXPR
+                  || TREE_CODE (arg) == INDIRECT_REF)
              arg = TREE_OPERAND (arg, 0);
            if (arg != last_parm)
              warning ("second parameter of `va_start' not last named argument");
          }
-       else
+       else if (! current_function_varargs)
          /* Evidently an out of date version of <stdarg.h>; can't validate
             va_start's second argument, but can still work as intended.  */
          warning ("`__builtin_next_arg' called without an argument");
@@ -7296,8 +8122,15 @@ expand_builtin (exp, target, subtarget, mode, ignore)
       if (arglist == 0)
        return const0_rtx;
       else
-       return (TREE_CODE_CLASS (TREE_CODE (TREE_VALUE (arglist))) == 'c'
-               ? const1_rtx : const0_rtx);
+       {
+         tree arg = TREE_VALUE (arglist);
+
+         STRIP_NOPS (arg);
+         return (TREE_CODE_CLASS (TREE_CODE (arg)) == 'c'
+                 || (TREE_CODE (arg) == ADDR_EXPR
+                     && TREE_CODE (TREE_OPERAND (arg, 0)) == STRING_CST)
+                 ? const1_rtx : const0_rtx);
+       }
 
     case BUILT_IN_FRAME_ADDRESS:
       /* The argument must be a nonnegative integer constant.
@@ -7322,52 +8155,17 @@ expand_builtin (exp, target, subtarget, mode, ignore)
        }
       else
        {
-         int count = TREE_INT_CST_LOW (TREE_VALUE (arglist)); 
-         rtx tem = frame_pointer_rtx;
-         int i;
-
-         /* Some machines need special handling before we can access arbitrary
-            frames.  For example, on the sparc, we must first flush all
-            register windows to the stack.  */
-#ifdef SETUP_FRAME_ADDRESSES
-         SETUP_FRAME_ADDRESSES ();
-#endif
-
-         /* On the sparc, the return address is not in the frame, it is
-            in a register.  There is no way to access it off of the current
-            frame pointer, but it can be accessed off the previous frame
-            pointer by reading the value from the register window save
-            area.  */
-#ifdef RETURN_ADDR_IN_PREVIOUS_FRAME
-         if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_RETURN_ADDRESS)
-           count--;
-#endif
-
-         /* Scan back COUNT frames to the specified frame.  */
-         for (i = 0; i < count; i++)
-           {
-             /* Assume the dynamic chain pointer is in the word that
-                the frame address points to, unless otherwise specified.  */
-#ifdef DYNAMIC_CHAIN_ADDRESS
-             tem = DYNAMIC_CHAIN_ADDRESS (tem);
-#endif
-             tem = memory_address (Pmode, tem);
-             tem = copy_to_reg (gen_rtx (MEM, Pmode, tem));
-           }
+         rtx tem = expand_builtin_return_addr (DECL_FUNCTION_CODE (fndecl),
+                                               TREE_INT_CST_LOW (TREE_VALUE (arglist)),
+                                               hard_frame_pointer_rtx);
 
          /* For __builtin_frame_address, return what we've got.  */
          if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FRAME_ADDRESS)
            return tem;
 
-         /* For __builtin_return_address,
-            Get the return address from that frame.  */
-#ifdef RETURN_ADDR_RTX
-         return RETURN_ADDR_RTX (count, tem);
-#else
-         tem = memory_address (Pmode,
-                               plus_constant (tem, GET_MODE_SIZE (Pmode)));
-         return copy_to_reg (gen_rtx (MEM, Pmode, tem));
-#endif
+         if (GET_CODE (tem) != REG)
+           tem = copy_to_reg (tem);
+         return tem;
        }
 
     case BUILT_IN_ALLOCA:
@@ -7458,7 +8256,7 @@ expand_builtin (exp, target, subtarget, mode, ignore)
            result = gen_reg_rtx (insn_mode);
 
          src_rtx = memory_address (BLKmode,
-                                   expand_expr (src, NULL_RTX, Pmode,
+                                   expand_expr (src, NULL_RTX, ptr_mode,
                                                 EXPAND_NORMAL));
          if (! (*insn_operand_predicate[(int)icode][1]) (src_rtx, Pmode))
            src_rtx = copy_to_mode_reg (Pmode, src_rtx);
@@ -7526,6 +8324,7 @@ expand_builtin (exp, target, subtarget, mode, ignore)
          tree dest = TREE_VALUE (arglist);
          tree src = TREE_VALUE (TREE_CHAIN (arglist));
          tree len = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
+         tree type;
 
          int src_align
            = get_pointer_alignment (src, BIGGEST_ALIGNMENT) / BITS_PER_UNIT;
@@ -7542,20 +8341,30 @@ expand_builtin (exp, target, subtarget, mode, ignore)
              break;
            }
 
-         dest_rtx = expand_expr (dest, NULL_RTX, Pmode, EXPAND_NORMAL);
+         dest_rtx = expand_expr (dest, NULL_RTX, ptr_mode, EXPAND_SUM);
          dest_mem = gen_rtx (MEM, BLKmode,
                              memory_address (BLKmode, dest_rtx));
+         /* There could be a void* cast on top of the object.  */
+         while (TREE_CODE (dest) == NOP_EXPR)
+           dest = TREE_OPERAND (dest, 0);
+         type = TREE_TYPE (TREE_TYPE (dest));
+         MEM_IN_STRUCT_P (dest_mem) = AGGREGATE_TYPE_P (type);
          src_mem = gen_rtx (MEM, BLKmode,
                             memory_address (BLKmode,
                                             expand_expr (src, NULL_RTX,
-                                                         Pmode,
-                                                         EXPAND_NORMAL)));
+                                                         ptr_mode,
+                                                         EXPAND_SUM)));
+         /* There could be a void* cast on top of the object.  */
+         while (TREE_CODE (src) == NOP_EXPR)
+           src = TREE_OPERAND (src, 0);
+         type = TREE_TYPE (TREE_TYPE (src));
+         MEM_IN_STRUCT_P (src_mem) = AGGREGATE_TYPE_P (type);
 
          /* Copy word part most expediently.  */
          emit_block_move (dest_mem, src_mem,
                           expand_expr (len, NULL_RTX, VOIDmode, 0),
                           MIN (src_align, dest_align));
-         return dest_rtx;
+         return force_operand (dest_rtx, NULL_RTX);
        }
 
 /* These comparison functions need an instruction that returns an actual
@@ -7660,10 +8469,12 @@ expand_builtin (exp, target, subtarget, mode, ignore)
 
        emit_insn (gen_cmpstrsi (result,
                                 gen_rtx (MEM, BLKmode,
-                                         expand_expr (arg1, NULL_RTX, Pmode,
+                                         expand_expr (arg1, NULL_RTX,
+                                                      ptr_mode,
                                                       EXPAND_NORMAL)),
                                 gen_rtx (MEM, BLKmode,
-                                         expand_expr (arg2, NULL_RTX, Pmode,
+                                         expand_expr (arg2, NULL_RTX,
+                                                      ptr_mode,
                                                       EXPAND_NORMAL)),
                                 expand_expr (len, NULL_RTX, VOIDmode, 0),
                                 GEN_INT (MIN (arg1_align, arg2_align))));
@@ -7686,6 +8497,161 @@ expand_builtin (exp, target, subtarget, mode, ignore)
       break;
 #endif
 
+      /* __builtin_setjmp is passed a pointer to an array of five words
+        (not all will be used on all machines).  It operates similarly to
+        the C library function of the same name, but is more efficient.
+        Much of the code below (and for longjmp) is copied from the handling
+        of non-local gotos.
+
+        NOTE: This is intended for use by GNAT and will only work in
+        the method used by it.  This code will likely NOT survive to 
+        the GCC 2.8.0 release.  */
+    case BUILT_IN_SETJMP:
+      if (arglist == 0
+         || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE)
+       break;
+
+      {
+       rtx buf_addr
+         = force_reg (Pmode, expand_expr (TREE_VALUE (arglist), subtarget,
+                                          VOIDmode, 0));
+       rtx lab1 = gen_label_rtx (), lab2 = gen_label_rtx ();
+       enum machine_mode sa_mode = Pmode;
+       rtx stack_save;
+
+       if (target == 0 || GET_CODE (target) != REG
+           || REGNO (target) < FIRST_PSEUDO_REGISTER)
+         target = gen_reg_rtx (value_mode);
+
+       emit_queue ();
+
+       emit_note (NULL_PTR, NOTE_INSN_SETJMP);
+       current_function_calls_setjmp = 1;
+
+       /* We store the frame pointer and the address of lab1 in the buffer
+          and use the rest of it for the stack save area, which is
+          machine-dependent.  */
+       emit_move_insn (gen_rtx (MEM, Pmode, buf_addr),
+                       virtual_stack_vars_rtx);
+       emit_move_insn
+         (validize_mem (gen_rtx (MEM, Pmode,
+                                 plus_constant (buf_addr,
+                                                GET_MODE_SIZE (Pmode)))),
+          gen_rtx (LABEL_REF, Pmode, lab1));
+
+#ifdef HAVE_save_stack_nonlocal
+       if (HAVE_save_stack_nonlocal)
+         sa_mode = insn_operand_mode[(int) CODE_FOR_save_stack_nonlocal][0];
+#endif
+
+       stack_save = gen_rtx (MEM, sa_mode,
+                             plus_constant (buf_addr,
+                                            2 * GET_MODE_SIZE (Pmode)));
+       emit_stack_save (SAVE_NONLOCAL, &stack_save, NULL_RTX);
+
+       /* Set TARGET to zero and branch around the other case.  */
+       emit_move_insn (target, const0_rtx);
+       emit_jump_insn (gen_jump (lab2));
+       emit_barrier ();
+       emit_label (lab1);
+
+       /* Now put in the code to restore the frame pointer, and argument
+          pointer, if needed.  The code below is from expand_end_bindings
+          in stmt.c; see detailed documentation there.  */
+#ifdef HAVE_nonlocal_goto
+       if (! HAVE_nonlocal_goto)
+#endif
+         emit_move_insn (virtual_stack_vars_rtx, hard_frame_pointer_rtx);
+
+#if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+       if (fixed_regs[ARG_POINTER_REGNUM])
+         {
+#ifdef ELIMINABLE_REGS
+           static struct elims {int from, to;} elim_regs[] = ELIMINABLE_REGS;
+           int i;
+
+           for (i = 0; i < sizeof elim_regs / sizeof elim_regs[0]; i++)
+             if (elim_regs[i].from == ARG_POINTER_REGNUM
+                 && elim_regs[i].to == HARD_FRAME_POINTER_REGNUM)
+               break;
+
+           if (i == sizeof elim_regs / sizeof elim_regs [0])
+#endif
+             {
+               /* Now restore our arg pointer from the address at which it
+                  was saved in our stack frame.
+                  If there hasn't be space allocated for it yet, make
+                  some now.  */
+               if (arg_pointer_save_area == 0)
+                 arg_pointer_save_area
+                   = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0);
+               emit_move_insn (virtual_incoming_args_rtx,
+                               copy_to_reg (arg_pointer_save_area));
+             }
+         }
+#endif
+
+       /* The result to return is in the static chain pointer.  */
+       if (GET_MODE (static_chain_rtx) == GET_MODE (target))
+         emit_move_insn (target, static_chain_rtx);
+       else
+         convert_move (target, static_chain_rtx, 0);
+
+       emit_label (lab2);
+       return target;
+      }
+
+      /* __builtin_longjmp is passed a pointer to an array of five words
+        and a value to return.  It's similar to the C library longjmp
+        function but works with __builtin_setjmp above.  */
+    case BUILT_IN_LONGJMP:
+      if (arglist == 0 || TREE_CHAIN (arglist) == 0
+         || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != POINTER_TYPE)
+       break;
+
+      {
+       rtx buf_addr
+         = force_reg (Pmode, expand_expr (TREE_VALUE (arglist), NULL_RTX,
+                                          VOIDmode, 0));
+       rtx fp = gen_rtx (MEM, Pmode, buf_addr);
+       rtx lab = gen_rtx (MEM, Pmode,
+                          plus_constant (buf_addr, GET_MODE_SIZE (Pmode)));
+       enum machine_mode sa_mode
+#ifdef HAVE_save_stack_nonlocal
+         = (HAVE_save_stack_nonlocal
+            ? insn_operand_mode[(int) CODE_FOR_save_stack_nonlocal][0]
+            : Pmode);
+#else
+       = Pmode;
+#endif
+       rtx stack = gen_rtx (MEM, sa_mode,
+                            plus_constant (buf_addr,
+                                           2 * GET_MODE_SIZE (Pmode)));
+       rtx value = expand_expr (TREE_VALUE (TREE_CHAIN (arglist)), NULL_RTX,
+                                VOIDmode, 0);
+
+       /* Pick up FP, label, and SP from the block and jump.  This code is
+          from expand_goto in stmt.c; see there for detailed comments.  */
+#if HAVE_nonlocal_goto
+       if (HAVE_nonlocal_goto)
+         emit_insn (gen_nonlocal_goto (fp, lab, stack, value));
+      else
+#endif
+       {
+         emit_move_insn (hard_frame_pointer_rtx, fp);
+         emit_stack_restore (SAVE_NONLOCAL, stack, NULL_RTX);
+
+         /* Put in the static chain register the return value.  */
+         emit_move_insn (static_chain_rtx, value);
+         emit_insn (gen_rtx (USE, VOIDmode, hard_frame_pointer_rtx));
+         emit_insn (gen_rtx (USE, VOIDmode, stack_pointer_rtx));
+         emit_insn (gen_rtx (USE, VOIDmode, static_chain_rtx));
+         emit_indirect_jump (copy_to_reg (lab));
+       }
+
+       return const0_rtx;
+      }
+
     default:                   /* just do library call, if unknown builtin */
       error ("built-in function `%s' not currently supported",
             IDENTIFIER_POINTER (DECL_NAME (fndecl)));
@@ -7715,7 +8681,7 @@ static enum machine_mode apply_result_mode[FIRST_PSEUDO_REGISTER];
 
 /* For each register that may be used for calling a function, this
    gives the offset of that register into the block returned by
-   __bultin_apply_args.  0 indicates that the register is not
+   __builtin_apply_args.  0 indicates that the register is not
    used for calling a function. */
 static int apply_args_reg_offset[FIRST_PSEUDO_REGISTER];
 
@@ -7917,13 +8883,25 @@ expand_builtin_apply_args ()
   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
     if ((mode = apply_args_mode[regno]) != VOIDmode)
       {
+       rtx tem;
+
        align = GET_MODE_ALIGNMENT (mode) / BITS_PER_UNIT;
        if (size % align != 0)
          size = CEIL (size, align) * align;
+
+       tem = gen_rtx (REG, mode, INCOMING_REGNO (regno));
+
+#ifdef STACK_REGS
+        /* For reg-stack.c's stack register household.
+          Compare with a similar piece of code in function.c.  */
+
+        emit_insn (gen_rtx (USE, mode, tem));
+#endif
+
        emit_move_insn (change_address (registers, mode,
                                        plus_constant (XEXP (registers, 0),
                                                       size)),
-                       gen_rtx (REG, mode, INCOMING_REGNO (regno)));
+                       tem);
        size += GET_MODE_SIZE (mode);
       }
 
@@ -8215,7 +9193,7 @@ expand_increment (exp, post)
   op0 = expand_expr (incremented, NULL_RTX, VOIDmode, 0);
 
   /* If OP0 is a SUBREG made for a promoted variable, we cannot increment
-     in place but intead must do sign- or zero-extension during assignment,
+     in place but instead must do sign- or zero-extension during assignment,
      so we copy it into a new register and let the code below use it as
      a copy.
 
@@ -8293,6 +9271,14 @@ expand_increment (exp, post)
                           TREE_TYPE (exp),
                           incremented,
                           TREE_OPERAND (exp, 1));
+
+      while (TREE_CODE (incremented) == NOP_EXPR
+            || TREE_CODE (incremented) == CONVERT_EXPR)
+       {
+         newexp = convert (TREE_TYPE (incremented), newexp);
+         incremented = TREE_OPERAND (incremented, 0);
+       }
+
       temp = expand_assignment (incremented, newexp, ! post, 0);
       return post ? op0 : temp;
     }
@@ -8395,6 +9381,7 @@ preexpand_calls (exp)
     case BLOCK:
     case RTL_EXPR:
     case WITH_CLEANUP_EXPR:
+    case CLEANUP_POINT_EXPR:
       return;
 
     case SAVE_EXPR:
@@ -8429,7 +9416,8 @@ void
 clear_pending_stack_adjust ()
 {
 #ifdef EXIT_IGNORE_STACK
-  if (! flag_omit_frame_pointer && EXIT_IGNORE_STACK
+  if (optimize > 0
+      && ! flag_omit_frame_pointer && EXIT_IGNORE_STACK
       && ! (DECL_INLINE (current_function_decl) && ! flag_no_inline)
       && ! flag_inline_functions)
     pending_stack_adjust = 0;
@@ -8475,6 +9463,10 @@ defer_cleanups_to (old_cleanups)
       /* reverse them so that we can build them in the right order.  */
       cleanups = nreverse (cleanups);
 
+      /* All cleanups must be on the function_obstack.  */
+      push_obstacks_nochange ();
+      resume_temporary_allocation ();
+
       while (cleanups)
        {
          if (new_cleanups)
@@ -8485,6 +9477,8 @@ defer_cleanups_to (old_cleanups)
 
          cleanups = TREE_CHAIN (cleanups);
        }
+
+      pop_obstacks ();
     }
 
   return new_cleanups;
@@ -8680,17 +9674,24 @@ do_jump (exp, if_false_label, if_true_label)
            emit_move_insn (flag, const1_rtx);
            emit_insns (seq2);
 
+           /* All cleanups must be on the function_obstack.  */
+           push_obstacks_nochange ();
+           resume_temporary_allocation ();
+
            /* convert flag, which is an rtx, into a tree. */
            cond = make_node (RTL_EXPR);
            TREE_TYPE (cond) = integer_type_node;
            RTL_EXPR_RTL (cond) = flag;
            RTL_EXPR_SEQUENCE (cond) = NULL_RTX;
+           cond = save_expr (cond);
 
            new_cleanups = build (COND_EXPR, void_type_node,
                                  truthvalue_conversion (cond),
                                  cleanups, integer_zero_node);
            new_cleanups = fold (new_cleanups);
 
+           pop_obstacks ();
+
            /* Now add in the conditionalized cleanups. */
            cleanups_this_call
              = tree_cons (NULL_TREE, new_cleanups, cleanups_this_call);
@@ -8737,17 +9738,24 @@ do_jump (exp, if_false_label, if_true_label)
            emit_move_insn (flag, const1_rtx);
            emit_insns (seq2);
 
+           /* All cleanups must be on the function_obstack.  */
+           push_obstacks_nochange ();
+           resume_temporary_allocation ();
+
            /* convert flag, which is an rtx, into a tree. */
            cond = make_node (RTL_EXPR);
            TREE_TYPE (cond) = integer_type_node;
            RTL_EXPR_RTL (cond) = flag;
            RTL_EXPR_SEQUENCE (cond) = NULL_RTX;
+           cond = save_expr (cond);
 
            new_cleanups = build (COND_EXPR, void_type_node,
                                  truthvalue_conversion (cond),
                                  cleanups, integer_zero_node);
            new_cleanups = fold (new_cleanups);
 
+           pop_obstacks ();
+
            /* Now add in the conditionalized cleanups. */
            cleanups_this_call
              = tree_cons (NULL_TREE, new_cleanups, cleanups_this_call);
@@ -8829,32 +9837,72 @@ do_jump (exp, if_false_label, if_true_label)
       break;
 
     case EQ_EXPR:
-      if (integer_zerop (TREE_OPERAND (exp, 1)))
-       do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label);
-      else if (((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))
-                == MODE_INT)
-               && 
-               !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))))
-              || GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) == MODE_COMPLEX_FLOAT
-              || GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) == MODE_COMPLEX_INT)
-       do_jump_by_parts_equality (exp, if_false_label, if_true_label);
-      else
-       comparison = compare (exp, EQ, EQ);
-      break;
+      {
+       tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
+
+       if (integer_zerop (TREE_OPERAND (exp, 1)))
+         do_jump (TREE_OPERAND (exp, 0), if_true_label, if_false_label);
+       else if (GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_COMPLEX_FLOAT
+                || GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_COMPLEX_INT)
+         do_jump
+           (fold
+            (build (TRUTH_ANDIF_EXPR, TREE_TYPE (exp),
+                    fold (build (EQ_EXPR, TREE_TYPE (exp),
+                                 fold (build1 (REALPART_EXPR,
+                                               TREE_TYPE (inner_type),
+                                               TREE_OPERAND (exp, 0))),
+                                 fold (build1 (REALPART_EXPR,
+                                               TREE_TYPE (inner_type),
+                                               TREE_OPERAND (exp, 1))))),
+                    fold (build (EQ_EXPR, TREE_TYPE (exp),
+                                 fold (build1 (IMAGPART_EXPR,
+                                               TREE_TYPE (inner_type),
+                                               TREE_OPERAND (exp, 0))),
+                                 fold (build1 (IMAGPART_EXPR,
+                                               TREE_TYPE (inner_type),
+                                               TREE_OPERAND (exp, 1))))))),
+            if_false_label, if_true_label);
+       else if (GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_INT
+                && !can_compare_p (TYPE_MODE (inner_type)))
+         do_jump_by_parts_equality (exp, if_false_label, if_true_label);
+       else
+         comparison = compare (exp, EQ, EQ);
+       break;
+      }
 
     case NE_EXPR:
-      if (integer_zerop (TREE_OPERAND (exp, 1)))
-       do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label);
-      else if (((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))
-                == MODE_INT)
-               && 
-               !can_compare_p (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))))
-              || GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) == MODE_COMPLEX_FLOAT
-              || GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0)))) == MODE_COMPLEX_INT)
-       do_jump_by_parts_equality (exp, if_true_label, if_false_label);
-      else
-       comparison = compare (exp, NE, NE);
-      break;
+      {
+       tree inner_type = TREE_TYPE (TREE_OPERAND (exp, 0));
+
+       if (integer_zerop (TREE_OPERAND (exp, 1)))
+         do_jump (TREE_OPERAND (exp, 0), if_false_label, if_true_label);
+       else if (GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_COMPLEX_FLOAT
+                || GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_COMPLEX_INT)
+         do_jump
+           (fold
+            (build (TRUTH_ORIF_EXPR, TREE_TYPE (exp),
+                    fold (build (NE_EXPR, TREE_TYPE (exp),
+                                 fold (build1 (REALPART_EXPR,
+                                               TREE_TYPE (inner_type),
+                                               TREE_OPERAND (exp, 0))),
+                                 fold (build1 (REALPART_EXPR,
+                                               TREE_TYPE (inner_type),
+                                               TREE_OPERAND (exp, 1))))),
+                    fold (build (NE_EXPR, TREE_TYPE (exp),
+                                 fold (build1 (IMAGPART_EXPR,
+                                               TREE_TYPE (inner_type),
+                                               TREE_OPERAND (exp, 0))),
+                                 fold (build1 (IMAGPART_EXPR,
+                                               TREE_TYPE (inner_type),
+                                               TREE_OPERAND (exp, 1))))))),
+            if_false_label, if_true_label);
+       else if (GET_MODE_CLASS (TYPE_MODE (inner_type)) == MODE_INT
+                && !can_compare_p (TYPE_MODE (inner_type)))
+         do_jump_by_parts_equality (exp, if_true_label, if_false_label);
+       else
+         comparison = compare (exp, NE, NE);
+       break;
+      }
 
     case LT_EXPR:
       if ((GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_OPERAND (exp, 0))))
@@ -9022,7 +10070,7 @@ do_jump_by_parts_greater (exp, swap, if_false_label, if_true_label)
    UNSIGNEDP says to do unsigned comparison.
    Jump to IF_TRUE_LABEL if OP0 is greater, IF_FALSE_LABEL otherwise.  */
 
-static void
+void
 do_jump_by_parts_greater_rtx (mode, unsignedp, op0, op1, if_false_label, if_true_label)
      enum machine_mode mode;
      int unsignedp;