OSDN Git Service

* ChangeLog: Additional fixes for AVX2 ChangeLog entry.
[pf3gnuchains/gcc-fork.git] / gcc / function.c
index ca57889..c94680c 100644 (file)
@@ -1,7 +1,7 @@
 /* Expands front end tree to back end RTL for GCC.
    Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
-   1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
-   Free Software Foundation, Inc.
+   1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
+   2010, 2011  Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -37,7 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
-#include "rtl.h"
+#include "rtl-error.h"
 #include "tree.h"
 #include "flags.h"
 #include "except.h"
@@ -51,13 +51,13 @@ along with GCC; see the file COPYING3.  If not see
 #include "recog.h"
 #include "output.h"
 #include "basic-block.h"
-#include "toplev.h"
 #include "hashtab.h"
 #include "ggc.h"
 #include "tm_p.h"
 #include "integrate.h"
 #include "langhooks.h"
 #include "target.h"
+#include "common/common-target.h"
 #include "cfglayout.h"
 #include "gimple.h"
 #include "tree-pass.h"
@@ -132,7 +132,7 @@ static GTY((if_marked ("ggc_marked_p"), param_is (struct rtx_def)))
 \f
 
 htab_t types_used_by_vars_hash = NULL;
-tree types_used_by_cur_var_decl = NULL;
+VEC(tree,gc) *types_used_by_cur_var_decl;
 
 /* Forward declarations.  */
 
@@ -212,8 +212,7 @@ free_after_compilation (struct function *f)
   prologue_insn_hash = NULL;
   epilogue_insn_hash = NULL;
 
-  if (crtl->emit.regno_pointer_align)
-    free (crtl->emit.regno_pointer_align);
+  free (crtl->emit.regno_pointer_align);
 
   memset (crtl, 0, sizeof (struct rtl_data));
   f->eh = NULL;
@@ -243,7 +242,7 @@ get_frame_size (void)
 
 bool
 frame_offset_overflow (HOST_WIDE_INT offset, tree func)
-{  
+{
   unsigned HOST_WIDE_INT size = FRAME_GROWS_DOWNWARD ? -offset : offset;
 
   if (size > ((unsigned HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (Pmode) - 1))
@@ -278,6 +277,75 @@ get_stack_local_alignment (tree type, enum machine_mode mode)
   return STACK_SLOT_ALIGNMENT (type, mode, alignment);
 }
 
+/* Determine whether it is possible to fit a stack slot of size SIZE and
+   alignment ALIGNMENT into an area in the stack frame that starts at
+   frame offset START and has a length of LENGTH.  If so, store the frame
+   offset to be used for the stack slot in *POFFSET and return true;
+   return false otherwise.  This function will extend the frame size when
+   given a start/length pair that lies at the end of the frame.  */
+
+static bool
+try_fit_stack_local (HOST_WIDE_INT start, HOST_WIDE_INT length,
+                    HOST_WIDE_INT size, unsigned int alignment,
+                    HOST_WIDE_INT *poffset)
+{
+  HOST_WIDE_INT this_frame_offset;
+  int frame_off, frame_alignment, frame_phase;
+
+  /* Calculate how many bytes the start of local variables is off from
+     stack alignment.  */
+  frame_alignment = PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT;
+  frame_off = STARTING_FRAME_OFFSET % frame_alignment;
+  frame_phase = frame_off ? frame_alignment - frame_off : 0;
+
+  /* Round the frame offset to the specified alignment.  */
+
+  /*  We must be careful here, since FRAME_OFFSET might be negative and
+      division with a negative dividend isn't as well defined as we might
+      like.  So we instead assume that ALIGNMENT is a power of two and
+      use logical operations which are unambiguous.  */
+  if (FRAME_GROWS_DOWNWARD)
+    this_frame_offset
+      = (FLOOR_ROUND (start + length - size - frame_phase,
+                     (unsigned HOST_WIDE_INT) alignment)
+        + frame_phase);
+  else
+    this_frame_offset
+      = (CEIL_ROUND (start - frame_phase,
+                    (unsigned HOST_WIDE_INT) alignment)
+        + frame_phase);
+
+  /* See if it fits.  If this space is at the edge of the frame,
+     consider extending the frame to make it fit.  Our caller relies on
+     this when allocating a new slot.  */
+  if (frame_offset == start && this_frame_offset < frame_offset)
+    frame_offset = this_frame_offset;
+  else if (this_frame_offset < start)
+    return false;
+  else if (start + length == frame_offset
+          && this_frame_offset + size > start + length)
+    frame_offset = this_frame_offset + size;
+  else if (this_frame_offset + size > start + length)
+    return false;
+
+  *poffset = this_frame_offset;
+  return true;
+}
+
+/* Create a new frame_space structure describing free space in the stack
+   frame beginning at START and ending at END, and chain it into the
+   function's frame_space_list.  */
+
+static void
+add_frame_space (HOST_WIDE_INT start, HOST_WIDE_INT end)
+{
+  struct frame_space *space = ggc_alloc_frame_space ();
+  space->next = crtl->frame_space_list;
+  crtl->frame_space_list = space;
+  space->start = start;
+  space->length = end - start;
+}
+
 /* Allocate a stack slot of SIZE bytes and return a MEM rtx for it
    with machine mode MODE.
 
@@ -287,19 +355,22 @@ get_stack_local_alignment (tree type, enum machine_mode mode)
    -2 means use BITS_PER_UNIT,
    positive specifies alignment boundary in bits.
 
-   If REDUCE_ALIGNMENT_OK is true, it is OK to reduce alignment.
+   KIND has ASLK_REDUCE_ALIGN bit set if it is OK to reduce
+   alignment and ASLK_RECORD_PAD bit set if we should remember
+   extra space we allocated for alignment purposes.  When we are
+   called from assign_stack_temp_for_type, it is not set so we don't
+   track the same stack slot in two independent lists.
 
    We do not round to stack_boundary here.  */
 
 rtx
 assign_stack_local_1 (enum machine_mode mode, HOST_WIDE_INT size,
-                     int align,
-                     bool reduce_alignment_ok ATTRIBUTE_UNUSED)
+                     int align, int kind)
 {
   rtx x, addr;
   int bigend_correction = 0;
+  HOST_WIDE_INT slot_offset = 0, old_frame_offset;
   unsigned int alignment, alignment_in_bits;
-  int frame_off, frame_alignment, frame_phase;
 
   if (align == 0)
     {
@@ -318,9 +389,6 @@ assign_stack_local_1 (enum machine_mode mode, HOST_WIDE_INT size,
 
   alignment_in_bits = alignment * BITS_PER_UNIT;
 
-  if (FRAME_GROWS_DOWNWARD)
-    frame_offset -= size;
-
   /* Ignore alignment if it exceeds MAX_SUPPORTED_STACK_ALIGNMENT.  */
   if (alignment_in_bits > MAX_SUPPORTED_STACK_ALIGNMENT)
     {
@@ -347,7 +415,7 @@ assign_stack_local_1 (enum machine_mode mode, HOST_WIDE_INT size,
                  /* It is OK to reduce the alignment as long as the
                     requested size is 0 or the estimated stack
                     alignment >= mode alignment.  */
-                 gcc_assert (reduce_alignment_ok
+                 gcc_assert ((kind & ASLK_REDUCE_ALIGN)
                              || size == 0
                              || (crtl->stack_alignment_estimated
                                  >= GET_MODE_ALIGNMENT (mode)));
@@ -363,35 +431,64 @@ assign_stack_local_1 (enum machine_mode mode, HOST_WIDE_INT size,
   if (crtl->max_used_stack_slot_alignment < alignment_in_bits)
     crtl->max_used_stack_slot_alignment = alignment_in_bits;
 
-  /* Calculate how many bytes the start of local variables is off from
-     stack alignment.  */
-  frame_alignment = PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT;
-  frame_off = STARTING_FRAME_OFFSET % frame_alignment;
-  frame_phase = frame_off ? frame_alignment - frame_off : 0;
+  if (mode != BLKmode || size != 0)
+    {
+      if (kind & ASLK_RECORD_PAD)
+       {
+         struct frame_space **psp;
 
-  /* Round the frame offset to the specified alignment.  The default is
-     to always honor requests to align the stack but a port may choose to
-     do its own stack alignment by defining STACK_ALIGNMENT_NEEDED.  */
-  if (STACK_ALIGNMENT_NEEDED
-      || mode != BLKmode
-      || size != 0)
+         for (psp = &crtl->frame_space_list; *psp; psp = &(*psp)->next)
+           {
+             struct frame_space *space = *psp;
+             if (!try_fit_stack_local (space->start, space->length, size,
+                                       alignment, &slot_offset))
+               continue;
+             *psp = space->next;
+             if (slot_offset > space->start)
+               add_frame_space (space->start, slot_offset);
+             if (slot_offset + size < space->start + space->length)
+               add_frame_space (slot_offset + size,
+                                space->start + space->length);
+             goto found_space;
+           }
+       }
+    }
+  else if (!STACK_ALIGNMENT_NEEDED)
     {
-      /*  We must be careful here, since FRAME_OFFSET might be negative and
-         division with a negative dividend isn't as well defined as we might
-         like.  So we instead assume that ALIGNMENT is a power of two and
-         use logical operations which are unambiguous.  */
-      if (FRAME_GROWS_DOWNWARD)
-       frame_offset
-         = (FLOOR_ROUND (frame_offset - frame_phase,
-                         (unsigned HOST_WIDE_INT) alignment)
-            + frame_phase);
-      else
-       frame_offset
-         = (CEIL_ROUND (frame_offset - frame_phase,
-                        (unsigned HOST_WIDE_INT) alignment)
-            + frame_phase);
+      slot_offset = frame_offset;
+      goto found_space;
+    }
+
+  old_frame_offset = frame_offset;
+
+  if (FRAME_GROWS_DOWNWARD)
+    {
+      frame_offset -= size;
+      try_fit_stack_local (frame_offset, size, size, alignment, &slot_offset);
+
+      if (kind & ASLK_RECORD_PAD)
+       {
+         if (slot_offset > frame_offset)
+           add_frame_space (frame_offset, slot_offset);
+         if (slot_offset + size < old_frame_offset)
+           add_frame_space (slot_offset + size, old_frame_offset);
+       }
+    }
+  else
+    {
+      frame_offset += size;
+      try_fit_stack_local (old_frame_offset, size, size, alignment, &slot_offset);
+
+      if (kind & ASLK_RECORD_PAD)
+       {
+         if (slot_offset > old_frame_offset)
+           add_frame_space (old_frame_offset, slot_offset);
+         if (slot_offset + size < frame_offset)
+           add_frame_space (slot_offset + size, frame_offset);
+       }
     }
 
+ found_space:
   /* On a big-endian machine, if we are allocating more space than we will use,
      use the least significant bytes of those that are allocated.  */
   if (BYTES_BIG_ENDIAN && mode != BLKmode && GET_MODE_SIZE (mode) < size)
@@ -402,17 +499,14 @@ assign_stack_local_1 (enum machine_mode mode, HOST_WIDE_INT size,
   if (virtuals_instantiated)
     addr = plus_constant (frame_pointer_rtx,
                          trunc_int_for_mode
-                         (frame_offset + bigend_correction
+                         (slot_offset + bigend_correction
                           + STARTING_FRAME_OFFSET, Pmode));
   else
     addr = plus_constant (virtual_stack_vars_rtx,
                          trunc_int_for_mode
-                         (frame_offset + bigend_correction,
+                         (slot_offset + bigend_correction,
                           Pmode));
 
-  if (!FRAME_GROWS_DOWNWARD)
-    frame_offset += size;
-
   x = gen_rtx_MEM (mode, addr);
   set_mem_align (x, alignment_in_bits);
   MEM_NOTRAP_P (x) = 1;
@@ -431,7 +525,7 @@ assign_stack_local_1 (enum machine_mode mode, HOST_WIDE_INT size,
 rtx
 assign_stack_local (enum machine_mode mode, HOST_WIDE_INT size, int align)
 {
-  return assign_stack_local_1 (mode, size, align, false);
+  return assign_stack_local_1 (mode, size, align, ASLK_RECORD_PAD);
 }
 \f
 \f
@@ -600,7 +694,7 @@ static void
 insert_temp_slot_address (rtx address, struct temp_slot *temp_slot)
 {
   void **slot;
-  struct temp_slot_address_entry *t = GGC_NEW (struct temp_slot_address_entry);
+  struct temp_slot_address_entry *t = ggc_alloc_temp_slot_address_entry ();
   t->address = address;
   t->temp_slot = temp_slot;
   t->hash = temp_slot_address_compute_hash (t);
@@ -710,7 +804,7 @@ assign_stack_temp_for_type (enum machine_mode mode, HOST_WIDE_INT size,
   /* Try to find an available, already-allocated temporary of the proper
      mode which meets the size and alignment requirements.  Choose the
      smallest one with the closest alignment.
-   
+
      If assign_stack_temp is called outside of the tree->rtl expansion,
      we cannot reuse the stack slots (that may still refer to
      VIRTUAL_STACK_VARS_REGNUM).  */
@@ -752,7 +846,7 @@ assign_stack_temp_for_type (enum machine_mode mode, HOST_WIDE_INT size,
 
          if (best_p->size - rounded_size >= alignment)
            {
-             p = GGC_NEW (struct temp_slot);
+             p = ggc_alloc_temp_slot ();
              p->in_use = p->addr_taken = 0;
              p->size = best_p->size - rounded_size;
              p->base_offset = best_p->base_offset + rounded_size;
@@ -776,7 +870,7 @@ assign_stack_temp_for_type (enum machine_mode mode, HOST_WIDE_INT size,
     {
       HOST_WIDE_INT frame_offset_old = frame_offset;
 
-      p = GGC_NEW (struct temp_slot);
+      p = ggc_alloc_temp_slot ();
 
       /* We are passing an explicit alignment request to assign_stack_local.
         One side effect of that is assign_stack_local will not round SIZE
@@ -786,11 +880,13 @@ assign_stack_temp_for_type (enum machine_mode mode, HOST_WIDE_INT size,
         and round it now.  We also make sure ALIGNMENT is at least
         BIGGEST_ALIGNMENT.  */
       gcc_assert (mode != BLKmode || align == BIGGEST_ALIGNMENT);
-      p->slot = assign_stack_local (mode,
-                                   (mode == BLKmode
-                                    ? CEIL_ROUND (size, (int) align / BITS_PER_UNIT)
-                                    : size),
-                                   align);
+      p->slot = assign_stack_local_1 (mode,
+                                     (mode == BLKmode
+                                      ? CEIL_ROUND (size,
+                                                    (int) align
+                                                    / BITS_PER_UNIT)
+                                      : size),
+                                     align, 0);
 
       p->align = align;
 
@@ -846,8 +942,11 @@ assign_stack_temp_for_type (enum machine_mode mode, HOST_WIDE_INT size,
   if (type != 0)
     {
       MEM_VOLATILE_P (slot) = TYPE_VOLATILE (type);
-      MEM_SET_IN_STRUCT_P (slot, (AGGREGATE_TYPE_P (type)
-                                 || TREE_CODE (type) == COMPLEX_TYPE));
+      gcc_checking_assert (!MEM_SCALAR_P (slot) && !MEM_IN_STRUCT_P (slot));
+      if (AGGREGATE_TYPE_P (type) || TREE_CODE (type) == COMPLEX_TYPE)
+       MEM_IN_STRUCT_P (slot) = 1;
+      else
+       MEM_SCALAR_P (slot) = 1;
     }
   MEM_NOTRAP_P (slot) = 1;
 
@@ -1164,17 +1263,24 @@ void
 free_temp_slots (void)
 {
   struct temp_slot *p, *next;
+  bool some_available = false;
 
   for (p = *temp_slots_at_level (temp_slot_level); p; p = next)
     {
       next = p->next;
 
       if (!p->keep)
-       make_slot_available (p);
+       {
+         make_slot_available (p);
+         some_available = true;
+       }
     }
 
-  remove_unused_temp_slot_addresses ();
-  combine_temp_slots ();
+  if (some_available)
+    {
+      remove_unused_temp_slot_addresses ();
+      combine_temp_slots ();
+    }
 }
 
 /* Push deeper into the nesting level for stack temporaries.  */
@@ -1192,15 +1298,20 @@ void
 pop_temp_slots (void)
 {
   struct temp_slot *p, *next;
+  bool some_available = false;
 
   for (p = *temp_slots_at_level (temp_slot_level); p; p = next)
     {
       next = p->next;
       make_slot_available (p);
+      some_available = true;
     }
 
-  remove_unused_temp_slot_addresses ();
-  combine_temp_slots ();
+  if (some_available)
+    {
+      remove_unused_temp_slot_addresses ();
+      combine_temp_slots ();
+    }
 
   temp_slot_level--;
 }
@@ -1311,6 +1422,11 @@ instantiate_new_reg (rtx x, HOST_WIDE_INT *poffset)
 #endif
       offset = cfa_offset;
     }
+  else if (x == virtual_preferred_stack_boundary_rtx)
+    {
+      new_rtx = GEN_INT (crtl->preferred_stack_boundary / BITS_PER_UNIT);
+      offset = 0;
+    }
   else
     return NULL_RTX;
 
@@ -1377,16 +1493,7 @@ instantiate_virtual_regs_in_rtx (rtx *loc, void *data)
 static int
 safe_insn_predicate (int code, int operand, rtx x)
 {
-  const struct insn_operand_data *op_data;
-
-  if (code < 0)
-    return true;
-
-  op_data = &insn_data[code].operand[operand];
-  if (op_data->predicate == NULL)
-    return true;
-
-  return op_data->predicate (x, op_data->mode);
+  return code < 0 || insn_operand_matches ((enum insn_code) code, operand, x);
 }
 
 /* A subroutine of instantiate_virtual_regs.  Instantiate any virtual
@@ -1598,7 +1705,13 @@ instantiate_virtual_regs_in_insn (rtx insn)
       if (!safe_insn_predicate (insn_code, i, x))
        {
          start_sequence ();
-         x = force_reg (insn_data[insn_code].operand[i].mode, x);
+         if (REG_P (x))
+           {
+             gcc_assert (REGNO (x) <= LAST_VIRTUAL_REGISTER);
+             x = copy_to_reg (x);
+           }
+         else
+           x = force_reg (insn_data[insn_code].operand[i].mode, x);
          seq = get_insns ();
          end_sequence ();
          if (seq)
@@ -1679,8 +1792,21 @@ instantiate_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
   if (! EXPR_P (t))
     {
       *walk_subtrees = 0;
-      if (DECL_P (t) && DECL_RTL_SET_P (t))
-       instantiate_decl_rtl (DECL_RTL (t));
+      if (DECL_P (t))
+       {
+         if (DECL_RTL_SET_P (t))
+           instantiate_decl_rtl (DECL_RTL (t));
+         if (TREE_CODE (t) == PARM_DECL && DECL_NAMELESS (t)
+             && DECL_INCOMING_RTL (t))
+           instantiate_decl_rtl (DECL_INCOMING_RTL (t));
+         if ((TREE_CODE (t) == VAR_DECL
+              || TREE_CODE (t) == RESULT_DECL)
+             && DECL_HAS_VALUE_EXPR_P (t))
+           {
+             tree v = DECL_VALUE_EXPR (t);
+             walk_tree (&v, instantiate_expr, NULL, NULL);
+           }
+       }
     }
   return NULL;
 }
@@ -1693,7 +1819,7 @@ instantiate_decls_1 (tree let)
 {
   tree t;
 
-  for (t = BLOCK_VARS (let); t; t = TREE_CHAIN (t))
+  for (t = BLOCK_VARS (let); t; t = DECL_CHAIN (t))
     {
       if (DECL_RTL_SET_P (t))
        instantiate_decl_rtl (DECL_RTL (t));
@@ -1715,10 +1841,11 @@ instantiate_decls_1 (tree let)
 static void
 instantiate_decls (tree fndecl)
 {
-  tree decl, t, next;
+  tree decl;
+  unsigned ix;
 
   /* Process all parameters of the function.  */
-  for (decl = DECL_ARGUMENTS (fndecl); decl; decl = TREE_CHAIN (decl))
+  for (decl = DECL_ARGUMENTS (fndecl); decl; decl = DECL_CHAIN (decl))
     {
       instantiate_decl_rtl (DECL_RTL (decl));
       instantiate_decl_rtl (DECL_INCOMING_RTL (decl));
@@ -1729,19 +1856,25 @@ instantiate_decls (tree fndecl)
        }
     }
 
-  /* Now process all variables defined in the function or its subblocks.  */
-  instantiate_decls_1 (DECL_INITIAL (fndecl));
-
-  t = cfun->local_decls;
-  cfun->local_decls = NULL_TREE;
-  for (; t; t = next)
+  if ((decl = DECL_RESULT (fndecl))
+      && TREE_CODE (decl) == RESULT_DECL)
     {
-      next = TREE_CHAIN (t);
-      decl = TREE_VALUE (t);
       if (DECL_RTL_SET_P (decl))
        instantiate_decl_rtl (DECL_RTL (decl));
-      ggc_free (t);
+      if (DECL_HAS_VALUE_EXPR_P (decl))
+       {
+         tree v = DECL_VALUE_EXPR (decl);
+         walk_tree (&v, instantiate_expr, NULL, NULL);
+       }
     }
+
+  /* Now process all variables defined in the function or its subblocks.  */
+  instantiate_decls_1 (DECL_INITIAL (fndecl));
+
+  FOR_EACH_LOCAL_DECL (cfun, ix, decl)
+    if (DECL_RTL_SET_P (decl))
+      instantiate_decl_rtl (DECL_RTL (decl));
+  VEC_free (tree, gc, cfun->local_decls);
 }
 
 /* Pass through the INSNS of function FNDECL and convert virtual register
@@ -1804,6 +1937,7 @@ instantiate_virtual_regs (void)
   /* Indicate that, from now on, assign_stack_local should use
      frame_pointer_rtx.  */
   virtuals_instantiated = 1;
+
   return 0;
 }
 
@@ -1822,7 +1956,7 @@ struct rtl_opt_pass pass_instantiate_virtual_regs =
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
-  TODO_dump_func                        /* todo_flags_finish */
+  0                                     /* todo_flags_finish */
  }
 };
 
@@ -1835,67 +1969,64 @@ struct rtl_opt_pass pass_instantiate_virtual_regs =
 int
 aggregate_value_p (const_tree exp, const_tree fntype)
 {
+  const_tree type = (TYPE_P (exp)) ? exp : TREE_TYPE (exp);
   int i, regno, nregs;
   rtx reg;
 
-  const_tree type = (TYPE_P (exp)) ? exp : TREE_TYPE (exp);
-
-  /* DECL node associated with FNTYPE when relevant, which we might need to
-     check for by-invisible-reference returns, typically for CALL_EXPR input
-     EXPressions.  */
-  const_tree fndecl = NULL_TREE;
-  
   if (fntype)
     switch (TREE_CODE (fntype))
       {
       case CALL_EXPR:
-       fndecl = get_callee_fndecl (fntype);
-       fntype = (fndecl
-                 ? TREE_TYPE (fndecl)
-                 : TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (fntype))));
+       {
+         tree fndecl = get_callee_fndecl (fntype);
+         fntype = (fndecl
+                   ? TREE_TYPE (fndecl)
+                   : TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (fntype))));
+       }
        break;
       case FUNCTION_DECL:
-       fndecl = fntype;
-       fntype = TREE_TYPE (fndecl);
+       fntype = TREE_TYPE (fntype);
        break;
       case FUNCTION_TYPE:
       case METHOD_TYPE:
         break;
       case IDENTIFIER_NODE:
-       fntype = 0;
+       fntype = NULL_TREE;
        break;
       default:
-       /* We don't expect other rtl types here.  */
+       /* We don't expect other tree types here.  */
        gcc_unreachable ();
       }
 
-  if (TREE_CODE (type) == VOID_TYPE)
+  if (VOID_TYPE_P (type))
     return 0;
 
+  /* If a record should be passed the same as its first (and only) member
+     don't pass it as an aggregate.  */
+  if (TREE_CODE (type) == RECORD_TYPE && TYPE_TRANSPARENT_AGGR (type))
+    return aggregate_value_p (first_field (type), fntype);
+
   /* If the front end has decided that this needs to be passed by
      reference, do so.  */
   if ((TREE_CODE (exp) == PARM_DECL || TREE_CODE (exp) == RESULT_DECL)
       && DECL_BY_REFERENCE (exp))
     return 1;
 
-  /* If the EXPression is a CALL_EXPR, honor DECL_BY_REFERENCE set on the
-     called function RESULT_DECL, meaning the function returns in memory by
-     invisible reference.  This check lets front-ends not set TREE_ADDRESSABLE
-     on the function type, which used to be the way to request such a return
-     mechanism but might now be causing troubles at gimplification time if
-     temporaries with the function type need to be created.  */
-  if (TREE_CODE (exp) == CALL_EXPR && fndecl && DECL_RESULT (fndecl)
-      && DECL_BY_REFERENCE (DECL_RESULT (fndecl)))
-    return 1;
-      
-  if (targetm.calls.return_in_memory (type, fntype))
+  /* Function types that are TREE_ADDRESSABLE force return in memory.  */
+  if (fntype && TREE_ADDRESSABLE (fntype))
     return 1;
+
   /* Types that are TREE_ADDRESSABLE must be constructed in memory,
      and thus can't be returned in registers.  */
   if (TREE_ADDRESSABLE (type))
     return 1;
+
   if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type))
     return 1;
+
+  if (targetm.calls.return_in_memory (type, fntype))
+    return 1;
+
   /* Make sure we have suitable call-clobbered regs to return
      the value in; if not, we must return it in memory.  */
   reg = hard_function_value (type, 0, fntype, 0);
@@ -1910,6 +2041,7 @@ aggregate_value_p (const_tree exp, const_tree fntype)
   for (i = 0; i < nregs; i++)
     if (! call_used_regs[regno + i])
       return 1;
+
   return 0;
 }
 \f
@@ -1921,7 +2053,7 @@ use_register_for_decl (const_tree decl)
 {
   if (!targetm.calls.allocate_stack_slots_for_args())
     return true;
-  
+
   /* Honor volatile.  */
   if (TREE_SIDE_EFFECTS (decl))
     return false;
@@ -1986,9 +2118,18 @@ pass_by_reference (CUMULATIVE_ARGS *ca, enum machine_mode mode,
       /* GCC post 3.4 passes *all* variable sized types by reference.  */
       if (!TYPE_SIZE (type) || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
        return true;
+
+      /* If a record type should be passed the same as its first (and only)
+        member, use the type and mode of that member.  */
+      if (TREE_CODE (type) == RECORD_TYPE && TYPE_TRANSPARENT_AGGR (type))
+       {
+         type = TREE_TYPE (first_field (type));
+         mode = TYPE_MODE (type);
+       }
     }
 
-  return targetm.calls.pass_by_reference (ca, mode, type, named_arg);
+  return targetm.calls.pass_by_reference (pack_cumulative_args (ca), mode,
+                                         type, named_arg);
 }
 
 /* Return true if TYPE, which is passed by reference, should be callee
@@ -2000,7 +2141,8 @@ reference_callee_copied (CUMULATIVE_ARGS *ca, enum machine_mode mode,
 {
   if (type && TREE_ADDRESSABLE (type))
     return false;
-  return targetm.calls.callee_copies (ca, mode, type, named_arg);
+  return targetm.calls.callee_copies (pack_cumulative_args (ca), mode, type,
+                                     named_arg);
 }
 
 /* Structures to communicate between the subroutines of assign_parms.
@@ -2009,7 +2151,10 @@ reference_callee_copied (CUMULATIVE_ARGS *ca, enum machine_mode mode,
 
 struct assign_parm_data_all
 {
-  CUMULATIVE_ARGS args_so_far;
+  /* When INIT_CUMULATIVE_ARGS gets revamped, allocating CUMULATIVE_ARGS
+     should become a job of the target or otherwise encapsulated.  */
+  CUMULATIVE_ARGS args_so_far_v;
+  cumulative_args_t args_so_far;
   struct args_size stack_args_size;
   tree function_result_decl;
   tree orig_fnargs;
@@ -2042,18 +2187,19 @@ struct assign_parm_data_one
 static void
 assign_parms_initialize_all (struct assign_parm_data_all *all)
 {
-  tree fntype;
+  tree fntype ATTRIBUTE_UNUSED;
 
   memset (all, 0, sizeof (*all));
 
   fntype = TREE_TYPE (current_function_decl);
 
 #ifdef INIT_CUMULATIVE_INCOMING_ARGS
-  INIT_CUMULATIVE_INCOMING_ARGS (all->args_so_far, fntype, NULL_RTX);
+  INIT_CUMULATIVE_INCOMING_ARGS (all->args_so_far_v, fntype, NULL_RTX);
 #else
-  INIT_CUMULATIVE_ARGS (all->args_so_far, fntype, NULL_RTX,
+  INIT_CUMULATIVE_ARGS (all->args_so_far_v, fntype, NULL_RTX,
                        current_function_decl, -1);
 #endif
+  all->args_so_far = pack_cumulative_args (&all->args_so_far_v);
 
 #ifdef REG_PARM_STACK_SPACE
   all->reg_parm_stack_space = REG_PARM_STACK_SPACE (current_function_decl);
@@ -2064,25 +2210,13 @@ assign_parms_initialize_all (struct assign_parm_data_all *all)
    entries of the component type.  Return a new list of substitutions are
    needed, else the old list.  */
 
-static tree
-split_complex_args (tree args)
+static void
+split_complex_args (VEC(tree, heap) **args)
 {
+  unsigned i;
   tree p;
 
-  /* Before allocating memory, check for the common case of no complex.  */
-  for (p = args; p; p = TREE_CHAIN (p))
-    {
-      tree type = TREE_TYPE (p);
-      if (TREE_CODE (type) == COMPLEX_TYPE
-         && targetm.calls.split_complex_arg (type))
-        goto found;
-    }
-  return args;
-
- found:
-  args = copy_list (args);
-
-  for (p = args; p; p = TREE_CHAIN (p))
+  FOR_EACH_VEC_ELT (tree, *args, i, p)
     {
       tree type = TREE_TYPE (p);
       if (TREE_CODE (type) == COMPLEX_TYPE
@@ -2093,6 +2227,7 @@ split_complex_args (tree args)
          bool addressable = TREE_ADDRESSABLE (p);
 
          /* Rewrite the PARM_DECL's type with its component.  */
+         p = copy_node (p);
          TREE_TYPE (p) = subtype;
          DECL_ARG_TYPE (p) = TREE_TYPE (DECL_ARG_TYPE (p));
          DECL_MODE (p) = VOIDmode;
@@ -2106,6 +2241,7 @@ split_complex_args (tree args)
          DECL_IGNORED_P (p) = addressable;
          TREE_ADDRESSABLE (p) = 0;
          layout_decl (p, 0);
+         VEC_replace (tree, *args, i, p);
 
          /* Build a second synthetic decl.  */
          decl = build_decl (EXPR_LOCATION (p),
@@ -2114,27 +2250,27 @@ split_complex_args (tree args)
          DECL_ARTIFICIAL (decl) = addressable;
          DECL_IGNORED_P (decl) = addressable;
          layout_decl (decl, 0);
-
-         /* Splice it in; skip the new decl.  */
-         TREE_CHAIN (decl) = TREE_CHAIN (p);
-         TREE_CHAIN (p) = decl;
-         p = decl;
+         VEC_safe_insert (tree, heap, *args, ++i, decl);
        }
     }
-
-  return args;
 }
 
 /* A subroutine of assign_parms.  Adjust the parameter list to incorporate
    the hidden struct return argument, and (abi willing) complex args.
    Return the new parameter list.  */
 
-static tree
+static VEC(tree, heap) *
 assign_parms_augmented_arg_list (struct assign_parm_data_all *all)
 {
   tree fndecl = current_function_decl;
   tree fntype = TREE_TYPE (fndecl);
-  tree fnargs = DECL_ARGUMENTS (fndecl);
+  VEC(tree, heap) *fnargs = NULL;
+  tree arg;
+
+  for (arg = DECL_ARGUMENTS (fndecl); arg; arg = DECL_CHAIN (arg))
+    VEC_safe_push (tree, heap, fnargs, arg);
+
+  all->orig_fnargs = DECL_ARGUMENTS (fndecl);
 
   /* If struct value address is treated as the first argument, make it so.  */
   if (aggregate_value_p (DECL_RESULT (fndecl), fndecl)
@@ -2145,21 +2281,22 @@ assign_parms_augmented_arg_list (struct assign_parm_data_all *all)
       tree decl;
 
       decl = build_decl (DECL_SOURCE_LOCATION (fndecl),
-                        PARM_DECL, NULL_TREE, type);
+                        PARM_DECL, get_identifier (".result_ptr"), type);
       DECL_ARG_TYPE (decl) = type;
       DECL_ARTIFICIAL (decl) = 1;
-      DECL_IGNORED_P (decl) = 1;
+      DECL_NAMELESS (decl) = 1;
+      TREE_CONSTANT (decl) = 1;
+
+      DECL_CHAIN (decl) = all->orig_fnargs;
+      all->orig_fnargs = decl;
+      VEC_safe_insert (tree, heap, fnargs, 0, decl);
 
-      TREE_CHAIN (decl) = fnargs;
-      fnargs = decl;
       all->function_result_decl = decl;
     }
 
-  all->orig_fnargs = fnargs;
-
   /* If the target wants to split complex arguments into scalars, do so.  */
   if (targetm.calls.split_complex_arg)
-    fnargs = split_complex_args (fnargs);
+    split_complex_args (&fnargs);
 
   return fnargs;
 }
@@ -2181,9 +2318,9 @@ assign_parm_find_data_types (struct assign_parm_data_all *all, tree parm,
   /* NAMED_ARG is a misnomer.  We really mean 'non-variadic'. */
   if (!cfun->stdarg)
     data->named_arg = 1;  /* No variadic parms.  */
-  else if (TREE_CHAIN (parm))
+  else if (DECL_CHAIN (parm))
     data->named_arg = 1;  /* Not the last non-variadic parm. */
-  else if (targetm.calls.strict_argument_naming (&all->args_so_far))
+  else if (targetm.calls.strict_argument_naming (all->args_so_far))
     data->named_arg = 1;  /* Only variadic ones are unnamed.  */
   else
     data->named_arg = 0;  /* Treat as variadic.  */
@@ -2210,15 +2347,16 @@ assign_parm_find_data_types (struct assign_parm_data_all *all, tree parm,
   passed_mode = TYPE_MODE (passed_type);
   nominal_mode = TYPE_MODE (nominal_type);
 
-  /* If the parm is to be passed as a transparent union, use the type of
-     the first field for the tests below.  We have already verified that
-     the modes are the same.  */
-  if (TREE_CODE (passed_type) == UNION_TYPE
-      && TYPE_TRANSPARENT_UNION (passed_type))
-    passed_type = TREE_TYPE (TYPE_FIELDS (passed_type));
+  /* If the parm is to be passed as a transparent union or record, use the
+     type of the first field for the tests below.  We have already verified
+     that the modes are the same.  */
+  if ((TREE_CODE (passed_type) == UNION_TYPE
+       || TREE_CODE (passed_type) == RECORD_TYPE)
+      && TYPE_TRANSPARENT_AGGR (passed_type))
+    passed_type = TREE_TYPE (first_field (passed_type));
 
   /* See if this arg was passed by invisible reference.  */
-  if (pass_by_reference (&all->args_so_far, passed_mode,
+  if (pass_by_reference (&all->args_so_far_v, passed_mode,
                         passed_type, data->named_arg))
     {
       passed_type = nominal_type = build_pointer_type (passed_type);
@@ -2247,7 +2385,7 @@ assign_parms_setup_varargs (struct assign_parm_data_all *all,
 {
   int varargs_pretend_bytes = 0;
 
-  targetm.calls.setup_incoming_varargs (&all->args_so_far,
+  targetm.calls.setup_incoming_varargs (all->args_so_far,
                                        data->promoted_mode,
                                        data->passed_type,
                                        &varargs_pretend_bytes, no_rtl);
@@ -2276,13 +2414,10 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
       return;
     }
 
-#ifdef FUNCTION_INCOMING_ARG
-  entry_parm = FUNCTION_INCOMING_ARG (all->args_so_far, data->promoted_mode,
-                                     data->passed_type, data->named_arg);
-#else
-  entry_parm = FUNCTION_ARG (all->args_so_far, data->promoted_mode,
-                            data->passed_type, data->named_arg);
-#endif
+  entry_parm = targetm.calls.function_incoming_arg (all->args_so_far,
+                                                   data->promoted_mode,
+                                                   data->passed_type,
+                                                   data->named_arg);
 
   if (entry_parm == 0)
     data->promoted_mode = data->passed_mode;
@@ -2303,16 +2438,12 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
 #endif
   if (!in_regs && !data->named_arg)
     {
-      if (targetm.calls.pretend_outgoing_varargs_named (&all->args_so_far))
+      if (targetm.calls.pretend_outgoing_varargs_named (all->args_so_far))
        {
          rtx tem;
-#ifdef FUNCTION_INCOMING_ARG
-         tem = FUNCTION_INCOMING_ARG (all->args_so_far, data->promoted_mode,
-                                      data->passed_type, true);
-#else
-         tem = FUNCTION_ARG (all->args_so_far, data->promoted_mode,
-                             data->passed_type, true);
-#endif
+         tem = targetm.calls.function_incoming_arg (all->args_so_far,
+                                                    data->promoted_mode,
+                                                    data->passed_type, true);
          in_regs = tem != NULL;
        }
     }
@@ -2327,7 +2458,7 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
     {
       int partial;
 
-      partial = targetm.calls.arg_partial_bytes (&all->args_so_far,
+      partial = targetm.calls.arg_partial_bytes (all->args_so_far,
                                                 data->promoted_mode,
                                                 data->passed_type,
                                                 data->named_arg);
@@ -2445,16 +2576,13 @@ assign_parm_find_stack_rtl (tree parm, struct assign_parm_data_one *data)
       if (data->promoted_mode != BLKmode
          && data->promoted_mode != DECL_MODE (parm))
        {
-         set_mem_size (stack_parm,
-                       GEN_INT (GET_MODE_SIZE (data->promoted_mode)));
-         if (MEM_EXPR (stack_parm) && MEM_OFFSET (stack_parm))
+         set_mem_size (stack_parm, GET_MODE_SIZE (data->promoted_mode));
+         if (MEM_EXPR (stack_parm) && MEM_OFFSET_KNOWN_P (stack_parm))
            {
              int offset = subreg_lowpart_offset (DECL_MODE (parm),
                                                  data->promoted_mode);
              if (offset)
-               set_mem_offset (stack_parm,
-                               plus_constant (MEM_OFFSET (stack_parm),
-                                              -offset));
+               set_mem_offset (stack_parm, MEM_OFFSET (stack_parm) - offset);
            }
        }
     }
@@ -2463,7 +2591,7 @@ assign_parm_find_stack_rtl (tree parm, struct assign_parm_data_one *data)
   align = BITS_PER_UNIT;
 
   /* If we're padding upward, we know that the alignment of the slot
-     is FUNCTION_ARG_BOUNDARY.  If we're using slot_offset, we're
+     is TARGET_FUNCTION_ARG_BOUNDARY.  If we're using slot_offset, we're
      intentionally forcing upward padding.  Otherwise we have to come
      up with a guess at the alignment based on OFFSET_RTX.  */
   if (data->locate.where_pad != downward || data->entry_parm)
@@ -2501,7 +2629,7 @@ assign_parm_adjust_entry_rtl (struct assign_parm_data_one *data)
         locations.  The Irix 6 ABI has examples of this.  */
       if (GET_CODE (entry_parm) == PARALLEL)
        emit_group_store (validize_mem (stack_parm), entry_parm,
-                         data->passed_type, 
+                         data->passed_type,
                          int_size_in_bytes (data->passed_type));
       else
        {
@@ -2626,7 +2754,7 @@ assign_parm_setup_block_p (struct assign_parm_data_one *data)
   return false;
 }
 
-/* A subroutine of assign_parms.  Arrange for the parameter to be 
+/* A subroutine of assign_parms.  Arrange for the parameter to be
    present and valid in DATA->STACK_RTL.  */
 
 static void
@@ -2741,9 +2869,7 @@ assign_parm_setup_block (struct assign_parm_data_all *all,
              int by = (UNITS_PER_WORD - size) * BITS_PER_UNIT;
              rtx reg = gen_rtx_REG (word_mode, REGNO (entry_parm));
 
-             x = expand_shift (LSHIFT_EXPR, word_mode, reg,
-                               build_int_cst (NULL_TREE, by),
-                               NULL_RTX, 1);
+             x = expand_shift (LSHIFT_EXPR, word_mode, reg, by, NULL_RTX, 1);
              tem = change_address (mem, word_mode, 0);
              emit_move_insn (tem, x);
            }
@@ -2769,6 +2895,17 @@ assign_parm_setup_block (struct assign_parm_data_all *all,
   SET_DECL_RTL (parm, stack_parm);
 }
 
+/* A subroutine of assign_parm_setup_reg, called through note_stores.
+   This collects sets and clobbers of hard registers in a HARD_REG_SET,
+   which is pointed to by DATA.  */
+static void
+record_hard_reg_sets (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+  HARD_REG_SET *pset = (HARD_REG_SET *)data;
+  if (REG_P (x) && HARD_REGISTER_P (x))
+    add_to_hard_reg_set (pset, GET_MODE (x), REGNO (x));
+}
+
 /* A subroutine of assign_parms.  Allocate a pseudo to hold the current
    parameter.  Get it there.  Perform all ABI specified conversions.  */
 
@@ -2776,10 +2913,12 @@ static void
 assign_parm_setup_reg (struct assign_parm_data_all *all, tree parm,
                       struct assign_parm_data_one *data)
 {
-  rtx parmreg;
+  rtx parmreg, validated_mem;
+  rtx equiv_stack_parm;
   enum machine_mode promoted_nominal_mode;
   int unsignedp = TYPE_UNSIGNED (TREE_TYPE (parm));
   bool did_conversion = false;
+  bool need_conversion, moved;
 
   /* Store the parm in a pseudoregister during the function, but we may
      need to do it in a wider mode.  Using 2 here makes the result
@@ -2808,11 +2947,19 @@ assign_parm_setup_reg (struct assign_parm_data_all *all, tree parm,
 
   /* Copy the value into the register, thus bridging between
      assign_parm_find_data_types and expand_expr_real_1.  */
-  if (data->nominal_mode != data->passed_mode
-      || promoted_nominal_mode != data->promoted_mode)
-    {
-      int save_tree_used;
 
+  equiv_stack_parm = data->stack_parm;
+  validated_mem = validize_mem (data->entry_parm);
+
+  need_conversion = (data->nominal_mode != data->passed_mode
+                    || promoted_nominal_mode != data->promoted_mode);
+  moved = false;
+
+  if (need_conversion
+      && GET_MODE_CLASS (data->nominal_mode) == MODE_INT
+      && data->nominal_mode == data->passed_mode
+      && data->nominal_mode == GET_MODE (data->entry_parm))
+    {
       /* ENTRY_PARM has been converted to PROMOTED_MODE, its
         mode, by the caller.  We now have to convert it to
         NOMINAL_MODE, if different.  However, PARMREG may be in
@@ -2828,13 +2975,71 @@ assign_parm_setup_reg (struct assign_parm_data_all *all, tree parm,
 
         In addition, the conversion may involve a call, which could
         clobber parameters which haven't been copied to pseudo
-        registers yet.  Therefore, we must first copy the parm to
-        a pseudo reg here, and save the conversion until after all
+        registers yet.
+
+        First, we try to emit an insn which performs the necessary
+        conversion.  We verify that this insn does not clobber any
+        hard registers.  */
+
+      enum insn_code icode;
+      rtx op0, op1;
+
+      icode = can_extend_p (promoted_nominal_mode, data->passed_mode,
+                           unsignedp);
+
+      op0 = parmreg;
+      op1 = validated_mem;
+      if (icode != CODE_FOR_nothing
+         && insn_operand_matches (icode, 0, op0)
+         && insn_operand_matches (icode, 1, op1))
+       {
+         enum rtx_code code = unsignedp ? ZERO_EXTEND : SIGN_EXTEND;
+         rtx insn, insns;
+         HARD_REG_SET hardregs;
+
+         start_sequence ();
+         insn = gen_extend_insn (op0, op1, promoted_nominal_mode,
+                                 data->passed_mode, unsignedp);
+         emit_insn (insn);
+         insns = get_insns ();
+
+         moved = true;
+         CLEAR_HARD_REG_SET (hardregs);
+         for (insn = insns; insn && moved; insn = NEXT_INSN (insn))
+           {
+             if (INSN_P (insn))
+               note_stores (PATTERN (insn), record_hard_reg_sets,
+                            &hardregs);
+             if (!hard_reg_set_empty_p (hardregs))
+               moved = false;
+           }
+
+         end_sequence ();
+
+         if (moved)
+           {
+             emit_insn (insns);
+             if (equiv_stack_parm != NULL_RTX)
+               equiv_stack_parm = gen_rtx_fmt_e (code, GET_MODE (parmreg),
+                                                 equiv_stack_parm);
+           }
+       }
+    }
+
+  if (moved)
+    /* Nothing to do.  */
+    ;
+  else if (need_conversion)
+    {
+      /* We did not have an insn to convert directly, or the sequence
+        generated appeared unsafe.  We must first copy the parm to a
+        pseudo reg, and save the conversion until after all
         parameters have been moved.  */
 
+      int save_tree_used;
       rtx tempreg = gen_reg_rtx (GET_MODE (data->entry_parm));
 
-      emit_move_insn (tempreg, validize_mem (data->entry_parm));
+      emit_move_insn (tempreg, validated_mem);
 
       push_to_sequence2 (all->first_conversion_insn, all->last_conversion_insn);
       tempreg = convert_to_mode (data->nominal_mode, tempreg, unsignedp);
@@ -2864,7 +3069,7 @@ assign_parm_setup_reg (struct assign_parm_data_all *all, tree parm,
       did_conversion = true;
     }
   else
-    emit_move_insn (parmreg, validize_mem (data->entry_parm));
+    emit_move_insn (parmreg, validated_mem);
 
   /* If we were passed a pointer but the actual value can safely live
      in a register, put it in one.  */
@@ -2949,7 +3154,7 @@ assign_parm_setup_reg (struct assign_parm_data_all *all, tree parm,
        }
       else if ((set = single_set (linsn)) != 0
               && SET_DEST (set) == parmreg)
-       set_unique_reg_note (linsn, REG_EQUIV, data->stack_parm);
+       set_unique_reg_note (linsn, REG_EQUIV, equiv_stack_parm);
     }
 
   /* For pointer data type, suggest pointer register.  */
@@ -2991,10 +3196,9 @@ assign_parm_setup_stack (struct assign_parm_data_all *all, tree parm,
          /* ??? This may need a big-endian conversion on sparc64.  */
          data->stack_parm
            = adjust_address (data->stack_parm, data->nominal_mode, 0);
-         if (offset && MEM_OFFSET (data->stack_parm))
+         if (offset && MEM_OFFSET_KNOWN_P (data->stack_parm))
            set_mem_offset (data->stack_parm,
-                           plus_constant (MEM_OFFSET (data->stack_parm),
-                                          offset));
+                           MEM_OFFSET (data->stack_parm) + offset);
        }
     }
 
@@ -3047,12 +3251,14 @@ assign_parm_setup_stack (struct assign_parm_data_all *all, tree parm,
    undo the frobbing that we did in assign_parms_augmented_arg_list.  */
 
 static void
-assign_parms_unsplit_complex (struct assign_parm_data_all *all, tree fnargs)
+assign_parms_unsplit_complex (struct assign_parm_data_all *all,
+                             VEC(tree, heap) *fnargs)
 {
   tree parm;
   tree orig_fnargs = all->orig_fnargs;
+  unsigned i = 0;
 
-  for (parm = orig_fnargs; parm; parm = TREE_CHAIN (parm))
+  for (parm = orig_fnargs; parm; parm = TREE_CHAIN (parm), ++i)
     {
       if (TREE_CODE (TREE_TYPE (parm)) == COMPLEX_TYPE
          && targetm.calls.split_complex_arg (TREE_TYPE (parm)))
@@ -3060,8 +3266,8 @@ assign_parms_unsplit_complex (struct assign_parm_data_all *all, tree fnargs)
          rtx tmp, real, imag;
          enum machine_mode inner = GET_MODE_INNER (DECL_MODE (parm));
 
-         real = DECL_RTL (fnargs);
-         imag = DECL_RTL (TREE_CHAIN (fnargs));
+         real = DECL_RTL (VEC_index (tree, fnargs, i));
+         imag = DECL_RTL (VEC_index (tree, fnargs, i + 1));
          if (inner != GET_MODE (real))
            {
              real = gen_lowpart_SUBREG (inner, real);
@@ -3094,8 +3300,8 @@ assign_parms_unsplit_complex (struct assign_parm_data_all *all, tree fnargs)
            tmp = gen_rtx_CONCAT (DECL_MODE (parm), real, imag);
          SET_DECL_RTL (parm, tmp);
 
-         real = DECL_INCOMING_RTL (fnargs);
-         imag = DECL_INCOMING_RTL (TREE_CHAIN (fnargs));
+         real = DECL_INCOMING_RTL (VEC_index (tree, fnargs, i));
+         imag = DECL_INCOMING_RTL (VEC_index (tree, fnargs, i + 1));
          if (inner != GET_MODE (real))
            {
              real = gen_lowpart_SUBREG (inner, real);
@@ -3103,20 +3309,8 @@ assign_parms_unsplit_complex (struct assign_parm_data_all *all, tree fnargs)
            }
          tmp = gen_rtx_CONCAT (DECL_MODE (parm), real, imag);
          set_decl_incoming_rtl (parm, tmp, false);
-         fnargs = TREE_CHAIN (fnargs);
+         i++;
        }
-      else
-       {
-         SET_DECL_RTL (parm, DECL_RTL (fnargs));
-         set_decl_incoming_rtl (parm, DECL_INCOMING_RTL (fnargs), false);
-
-         /* Set MEM_EXPR to the original decl, i.e. to PARM,
-            instead of the copy of decl, i.e. FNARGS.  */
-         if (DECL_INCOMING_RTL (parm) && MEM_P (DECL_INCOMING_RTL (parm)))
-           set_mem_expr (DECL_INCOMING_RTL (parm), parm);
-       }
-
-      fnargs = TREE_CHAIN (fnargs);
     }
 }
 
@@ -3127,7 +3321,9 @@ static void
 assign_parms (tree fndecl)
 {
   struct assign_parm_data_all all;
-  tree fnargs, parm;
+  tree parm;
+  VEC(tree, heap) *fnargs;
+  unsigned i;
 
   crtl->args.internal_arg_pointer
     = targetm.calls.internal_arg_pointer ();
@@ -3135,7 +3331,7 @@ assign_parms (tree fndecl)
   assign_parms_initialize_all (&all);
   fnargs = assign_parms_augmented_arg_list (&all);
 
-  for (parm = fnargs; parm; parm = TREE_CHAIN (parm))
+  FOR_EACH_VEC_ELT (tree, fnargs, i, parm)
     {
       struct assign_parm_data_one data;
 
@@ -3153,8 +3349,9 @@ assign_parms (tree fndecl)
       /* Estimate stack alignment from parameter alignment.  */
       if (SUPPORTS_STACK_ALIGNMENT)
         {
-          unsigned int align = FUNCTION_ARG_BOUNDARY (data.promoted_mode,
-                                                     data.passed_type);
+          unsigned int align
+           = targetm.calls.function_arg_boundary (data.promoted_mode,
+                                                  data.passed_type);
          align = MINIMUM_ALIGNMENT (data.passed_type, data.promoted_mode,
                                     align);
          if (TYPE_ALIGN (data.nominal_type) > align)
@@ -3167,8 +3364,8 @@ assign_parms (tree fndecl)
              crtl->stack_alignment_estimated = align;
            }
        }
-       
-      if (cfun->stdarg && !TREE_CHAIN (parm))
+
+      if (cfun->stdarg && !DECL_CHAIN (parm))
        assign_parms_setup_varargs (&all, &data, false);
 
       /* Find out where the parameter arrives in this function.  */
@@ -3182,11 +3379,19 @@ assign_parms (tree fndecl)
        }
 
       /* Record permanently how this parm was passed.  */
-      set_decl_incoming_rtl (parm, data.entry_parm, data.passed_pointer);
+      if (data.passed_pointer)
+       {
+         rtx incoming_rtl
+           = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (data.passed_type)),
+                          data.entry_parm);
+         set_decl_incoming_rtl (parm, incoming_rtl, true);
+       }
+      else
+       set_decl_incoming_rtl (parm, data.entry_parm, false);
 
       /* Update info on where next arg arrives in registers.  */
-      FUNCTION_ARG_ADVANCE (all.args_so_far, data.promoted_mode,
-                           data.passed_type, data.named_arg);
+      targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
+                                         data.passed_type, data.named_arg);
 
       assign_parm_adjust_stack_rtl (&data);
 
@@ -3198,9 +3403,11 @@ assign_parms (tree fndecl)
        assign_parm_setup_stack (&all, parm, &data);
     }
 
-  if (targetm.calls.split_complex_arg && fnargs != all.orig_fnargs)
+  if (targetm.calls.split_complex_arg)
     assign_parms_unsplit_complex (&all, fnargs);
 
+  VEC_free (tree, heap, fnargs);
+
   /* Output all parameter conversion instructions (possibly including calls)
      now that all parameters have been copied out of hard registers.  */
   emit_insn (all.first_conversion_insn);
@@ -3224,7 +3431,7 @@ assign_parms (tree fndecl)
                  crtl->stack_alignment_estimated = align;
                }
            }
-       } 
+       }
     }
 
   /* If we are receiving a struct value address as the first argument, set up
@@ -3238,13 +3445,22 @@ assign_parms (tree fndecl)
       rtx x;
 
       if (DECL_BY_REFERENCE (result))
-       x = addr;
+       {
+         SET_DECL_VALUE_EXPR (result, all.function_result_decl);
+         x = addr;
+       }
       else
        {
+         SET_DECL_VALUE_EXPR (result,
+                              build1 (INDIRECT_REF, TREE_TYPE (result),
+                                      all.function_result_decl));
          addr = convert_memory_address (Pmode, addr);
          x = gen_rtx_MEM (DECL_MODE (result), addr);
          set_mem_attributes (x, result, 1);
        }
+
+      DECL_HAS_VALUE_EXPR_P (result) = 1;
+
       SET_DECL_RTL (result, x);
     }
 
@@ -3277,13 +3493,14 @@ assign_parms (tree fndecl)
   /* See how many bytes, if any, of its args a function should try to pop
      on return.  */
 
-  crtl->args.pops_args = RETURN_POPS_ARGS (fndecl, TREE_TYPE (fndecl),
-                                                crtl->args.size);
+  crtl->args.pops_args = targetm.calls.return_pops_args (fndecl,
+                                                        TREE_TYPE (fndecl),
+                                                        crtl->args.size);
 
   /* For stdarg.h function, save info about
      regs and stack space used by the named args.  */
 
-  crtl->args.info = all.args_so_far;
+  crtl->args.info = all.args_so_far_v;
 
   /* Set the rtx used for the function return value.  Put this in its
      own variable so any optimizers that need this information don't have
@@ -3352,13 +3569,15 @@ gimple_seq
 gimplify_parameters (void)
 {
   struct assign_parm_data_all all;
-  tree fnargs, parm;
+  tree parm;
   gimple_seq stmts = NULL;
+  VEC(tree, heap) *fnargs;
+  unsigned i;
 
   assign_parms_initialize_all (&all);
   fnargs = assign_parms_augmented_arg_list (&all);
 
-  for (parm = fnargs; parm; parm = TREE_CHAIN (parm))
+  FOR_EACH_VEC_ELT (tree, fnargs, i, parm)
     {
       struct assign_parm_data_one data;
 
@@ -3370,8 +3589,8 @@ gimplify_parameters (void)
        continue;
 
       /* Update info on where next arg arrives in registers.  */
-      FUNCTION_ARG_ADVANCE (all.args_so_far, data.promoted_mode,
-                           data.passed_type, data.named_arg);
+      targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
+                                         data.passed_type, data.named_arg);
 
       /* ??? Once upon a time variable_size stuffed parameter list
         SAVE_EXPRs (amongst others) onto a pending sizes list.  This
@@ -3389,7 +3608,7 @@ gimplify_parameters (void)
       if (data.passed_pointer)
        {
           tree type = TREE_TYPE (data.passed_type);
-         if (reference_callee_copied (&all.args_so_far, TYPE_MODE (type),
+         if (reference_callee_copied (&all.args_so_far_v, TYPE_MODE (type),
                                       type, data.named_arg))
            {
              tree local, t;
@@ -3401,28 +3620,28 @@ gimplify_parameters (void)
                       && compare_tree_int (DECL_SIZE_UNIT (parm),
                                            STACK_CHECK_MAX_VAR_SIZE) > 0))
                {
-                 local = create_tmp_var (type, get_name (parm));
+                 local = create_tmp_reg (type, get_name (parm));
                  DECL_IGNORED_P (local) = 0;
                  /* If PARM was addressable, move that flag over
                     to the local copy, as its address will be taken,
-                    not the PARMs.  */
+                    not the PARMs.  Keep the parms address taken
+                    as we'll query that flag during gimplification.  */
                  if (TREE_ADDRESSABLE (parm))
-                   {
-                     TREE_ADDRESSABLE (parm) = 0;
-                     TREE_ADDRESSABLE (local) = 1;
-                   }
+                   TREE_ADDRESSABLE (local) = 1;
                }
              else
                {
                  tree ptr_type, addr;
 
                  ptr_type = build_pointer_type (type);
-                 addr = create_tmp_var (ptr_type, get_name (parm));
+                 addr = create_tmp_reg (ptr_type, get_name (parm));
                  DECL_IGNORED_P (addr) = 0;
                  local = build_fold_indirect_ref (addr);
 
                  t = built_in_decls[BUILT_IN_ALLOCA];
                  t = build_call_expr (t, 1, DECL_SIZE_UNIT (parm));
+                 /* The call has been built for a variable-sized object.  */
+                 CALL_ALLOCA_FOR_VAR_P (t) = 1;
                  t = fold_convert (ptr_type, t);
                  t = build2 (MODIFY_EXPR, TREE_TYPE (addr), addr, t);
                  gimplify_and_add (t, &stmts);
@@ -3436,6 +3655,8 @@ gimplify_parameters (void)
        }
     }
 
+  VEC_free (tree, heap, fnargs);
+
   return stmts;
 }
 \f
@@ -3457,9 +3678,10 @@ gimplify_parameters (void)
    FNDECL is the function in which the argument was defined.
 
    There are two types of rounding that are done.  The first, controlled by
-   FUNCTION_ARG_BOUNDARY, forces the offset from the start of the argument
-   list to be aligned to the specific boundary (in bits).  This rounding
-   affects the initial and starting offsets, but not the argument size.
+   TARGET_FUNCTION_ARG_BOUNDARY, forces the offset from the start of the
+   argument list to be aligned to the specific boundary (in bits).  This
+   rounding affects the initial and starting offsets, but not the argument
+   size.
 
    The second, controlled by FUNCTION_ARG_PADDING and PARM_BOUNDARY,
    optionally rounds the size of the parm to PARM_BOUNDARY.  The
@@ -3479,7 +3701,7 @@ locate_and_pad_parm (enum machine_mode passed_mode, tree type, int in_regs,
 {
   tree sizetree;
   enum direction where_pad;
-  unsigned int boundary;
+  unsigned int boundary, round_boundary;
   int reg_parm_stack_space = 0;
   int part_size_in_regs;
 
@@ -3510,7 +3732,9 @@ locate_and_pad_parm (enum machine_mode passed_mode, tree type, int in_regs,
   sizetree
     = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode));
   where_pad = FUNCTION_ARG_PADDING (passed_mode, type);
-  boundary = FUNCTION_ARG_BOUNDARY (passed_mode, type);
+  boundary = targetm.calls.function_arg_boundary (passed_mode, type);
+  round_boundary = targetm.calls.function_arg_round_boundary (passed_mode,
+                                                             type);
   locate->where_pad = where_pad;
 
   /* Alignment can't exceed MAX_SUPPORTED_STACK_ALIGNMENT.  */
@@ -3557,8 +3781,8 @@ locate_and_pad_parm (enum machine_mode passed_mode, tree type, int in_regs,
     tree s2 = sizetree;
     if (where_pad != none
        && (!host_integerp (sizetree, 1)
-           || (tree_low_cst (sizetree, 1) * BITS_PER_UNIT) % PARM_BOUNDARY))
-      s2 = round_up (s2, PARM_BOUNDARY / BITS_PER_UNIT);
+           || (tree_low_cst (sizetree, 1) * BITS_PER_UNIT) % round_boundary))
+      s2 = round_up (s2, round_boundary / BITS_PER_UNIT);
     SUB_PARM_SIZE (locate->slot_offset, s2);
   }
 
@@ -3610,8 +3834,8 @@ locate_and_pad_parm (enum machine_mode passed_mode, tree type, int in_regs,
 
   if (where_pad != none
       && (!host_integerp (sizetree, 1)
-         || (tree_low_cst (sizetree, 1) * BITS_PER_UNIT) % PARM_BOUNDARY))
-    sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT);
+         || (tree_low_cst (sizetree, 1) * BITS_PER_UNIT) % round_boundary))
+    sizetree = round_up (sizetree, round_boundary / BITS_PER_UNIT);
 
   ADD_PARM_SIZE (locate->size, sizetree);
 
@@ -3741,13 +3965,13 @@ setjmp_vars_warning (bitmap setjmp_crosses, tree block)
 {
   tree decl, sub;
 
-  for (decl = BLOCK_VARS (block); decl; decl = TREE_CHAIN (decl))
+  for (decl = BLOCK_VARS (block); decl; decl = DECL_CHAIN (decl))
     {
       if (TREE_CODE (decl) == VAR_DECL
          && DECL_RTL_SET_P (decl)
          && REG_P (DECL_RTL (decl))
          && regno_clobbered_at_setjmp (setjmp_crosses, REGNO (DECL_RTL (decl))))
-       warning (OPT_Wclobbered, "variable %q+D might be clobbered by" 
+       warning (OPT_Wclobbered, "variable %q+D might be clobbered by"
                  " %<longjmp%> or %<vfork%>", decl);
     }
 
@@ -3763,18 +3987,18 @@ setjmp_args_warning (bitmap setjmp_crosses)
 {
   tree decl;
   for (decl = DECL_ARGUMENTS (current_function_decl);
-       decl; decl = TREE_CHAIN (decl))
+       decl; decl = DECL_CHAIN (decl))
     if (DECL_RTL (decl) != 0
        && REG_P (DECL_RTL (decl))
        && regno_clobbered_at_setjmp (setjmp_crosses, REGNO (DECL_RTL (decl))))
-      warning (OPT_Wclobbered, 
+      warning (OPT_Wclobbered,
                "argument %q+D might be clobbered by %<longjmp%> or %<vfork%>",
               decl);
 }
 
 /* Generate warning messages for variables live across setjmp.  */
 
-void 
+void
 generate_setjmp_warnings (void)
 {
   bitmap setjmp_crosses = regstat_get_setjmp_crosses ();
@@ -3788,6 +4012,46 @@ generate_setjmp_warnings (void)
 }
 
 \f
+/* Reverse the order of elements in the fragment chain T of blocks,
+   and return the new head of the chain (old last element).  */
+
+static tree
+block_fragments_nreverse (tree t)
+{
+  tree prev = 0, block, next;
+  for (block = t; block; block = next)
+    {
+      next = BLOCK_FRAGMENT_CHAIN (block);
+      BLOCK_FRAGMENT_CHAIN (block) = prev;
+      prev = block;
+    }
+  return prev;
+}
+
+/* Reverse the order of elements in the chain T of blocks,
+   and return the new head of the chain (old last element).
+   Also do the same on subblocks and reverse the order of elements
+   in BLOCK_FRAGMENT_CHAIN as well.  */
+
+static tree
+blocks_nreverse_all (tree t)
+{
+  tree prev = 0, block, next;
+  for (block = t; block; block = next)
+    {
+      next = BLOCK_CHAIN (block);
+      BLOCK_CHAIN (block) = prev;
+      BLOCK_SUBBLOCKS (block) = blocks_nreverse_all (BLOCK_SUBBLOCKS (block));
+      if (BLOCK_FRAGMENT_CHAIN (block)
+         && BLOCK_FRAGMENT_ORIGIN (block) == NULL_TREE)
+       BLOCK_FRAGMENT_CHAIN (block)
+         = block_fragments_nreverse (BLOCK_FRAGMENT_CHAIN (block));
+      prev = block;
+    }
+  return prev;
+}
+
+
 /* Identify BLOCKs referenced by more than one NOTE_INSN_BLOCK_{BEG,END},
    and create duplicate blocks.  */
 /* ??? Need an option to either create block fragments or to create
@@ -3814,7 +4078,7 @@ reorder_blocks (void)
 
   /* Recreate the block tree from the note nesting.  */
   reorder_blocks_1 (get_insns (), block, &block_stack);
-  BLOCK_SUBBLOCKS (block) = blocks_nreverse (BLOCK_SUBBLOCKS (block));
+  BLOCK_SUBBLOCKS (block) = blocks_nreverse_all (BLOCK_SUBBLOCKS (block));
 
   VEC_free (tree, heap, block_stack);
 }
@@ -3846,9 +4110,8 @@ reorder_blocks_1 (rtx insns, tree current_block, VEC(tree,heap) **p_block_stack)
              tree block = NOTE_BLOCK (insn);
              tree origin;
 
-             origin = (BLOCK_FRAGMENT_ORIGIN (block)
-                       ? BLOCK_FRAGMENT_ORIGIN (block)
-                       : block);
+             gcc_assert (BLOCK_FRAGMENT_ORIGIN (block) == NULL_TREE);
+             origin = block;
 
              /* If we have seen this block before, that means it now
                 spans multiple address regions.  Create a new fragment.  */
@@ -3885,8 +4148,6 @@ reorder_blocks_1 (rtx insns, tree current_block, VEC(tree,heap) **p_block_stack)
          else if (NOTE_KIND (insn) == NOTE_INSN_BLOCK_END)
            {
              NOTE_BLOCK (insn) = VEC_pop (tree, *p_block_stack);
-             BLOCK_SUBBLOCKS (current_block)
-               = blocks_nreverse (BLOCK_SUBBLOCKS (current_block));
              current_block = BLOCK_SUPERCONTEXT (current_block);
            }
        }
@@ -3899,16 +4160,44 @@ reorder_blocks_1 (rtx insns, tree current_block, VEC(tree,heap) **p_block_stack)
 tree
 blocks_nreverse (tree t)
 {
-  tree prev = 0, decl, next;
-  for (decl = t; decl; decl = next)
+  tree prev = 0, block, next;
+  for (block = t; block; block = next)
     {
-      next = BLOCK_CHAIN (decl);
-      BLOCK_CHAIN (decl) = prev;
-      prev = decl;
+      next = BLOCK_CHAIN (block);
+      BLOCK_CHAIN (block) = prev;
+      prev = block;
     }
   return prev;
 }
 
+/* Concatenate two chains of blocks (chained through BLOCK_CHAIN)
+   by modifying the last node in chain 1 to point to chain 2.  */
+
+tree
+block_chainon (tree op1, tree op2)
+{
+  tree t1;
+
+  if (!op1)
+    return op2;
+  if (!op2)
+    return op1;
+
+  for (t1 = op1; BLOCK_CHAIN (t1); t1 = BLOCK_CHAIN (t1))
+    continue;
+  BLOCK_CHAIN (t1) = op2;
+
+#ifdef ENABLE_TREE_CHECKING
+  {
+    tree t2;
+    for (t2 = op2; t2; t2 = BLOCK_CHAIN (t2))
+      gcc_assert (t2 != t1);
+  }
+#endif
+
+  return op1;
+}
+
 /* Count the subblocks of the list starting with BLOCK.  If VECTOR is
    non-NULL, list them all into VECTOR, in a depth-first preorder
    traversal of the block tree.  Also clear TREE_ASM_WRITTEN in all
@@ -3988,7 +4277,7 @@ number_blocks (tree fn)
 
 /* If VAR is present in a subblock of BLOCK, return the subblock.  */
 
-tree
+DEBUG_FUNCTION tree
 debug_find_var_in_block_tree (tree var, tree block)
 {
   tree t;
@@ -4033,7 +4322,7 @@ invoke_set_current_function_hook (tree fndecl)
       if (optimization_current_node != opts)
        {
          optimization_current_node = opts;
-         cl_optimization_restore (TREE_OPTIMIZATION (opts));
+         cl_optimization_restore (&global_options, TREE_OPTIMIZATION (opts));
        }
 
       targetm.set_current_function (fndecl);
@@ -4076,11 +4365,18 @@ pop_cfun (void)
 
 /* Return value of funcdef and increase it.  */
 int
-get_next_funcdef_no (void) 
+get_next_funcdef_no (void)
 {
   return funcdef_no++;
 }
 
+/* Return value of funcdef.  */
+int
+get_last_funcdef_no (void)
+{
+  return funcdef_no;
+}
+
 /* Allocate a function structure for FNDECL and set its contents
    to the defaults.  Set cfun to the newly-allocated object.
    Some of the helper functions invoked during initialization assume
@@ -4100,9 +4396,7 @@ allocate_struct_function (tree fndecl, bool abstract_p)
   tree result;
   tree fntype = fndecl ? TREE_TYPE (fndecl) : NULL_TREE;
 
-  cfun = GGC_CNEW (struct function);
-
-  cfun->function_frequency = FUNCTION_FREQUENCY_NORMAL;
+  cfun = ggc_alloc_cleared_function ();
 
   init_eh_for_function ();
 
@@ -4130,15 +4424,15 @@ allocate_struct_function (tree fndecl, bool abstract_p)
          cfun->returns_struct = 1;
        }
 
-      cfun->stdarg
-       = (fntype
-          && TYPE_ARG_TYPES (fntype) != 0
-          && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
-              != void_type_node));
-      
+      cfun->stdarg = stdarg_p (fntype);
+
       /* Assume all registers in stdarg functions need to be saved.  */
       cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
       cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
+
+      /* ??? This could be set on a per-function basis by the front-end
+         but is this worth the hassle?  */
+      cfun->can_throw_non_call_exceptions = flag_non_call_exceptions;
     }
 }
 
@@ -4152,7 +4446,7 @@ push_struct_function (tree fndecl)
   allocate_struct_function (fndecl, false);
 }
 
-/* Reset cfun, and other non-struct-function variables to defaults as
+/* Reset crtl and other non-struct-function variables to defaults as
    appropriate for emitting rtl at the start of a function.  */
 
 static void
@@ -4165,6 +4459,12 @@ prepare_function_start (void)
   init_expr ();
   default_rtl_profile ();
 
+  if (flag_stack_usage_info)
+    {
+      cfun->su = ggc_alloc_cleared_stack_usage ();
+      cfun->su->static_stack_size = -1;
+    }
+
   cse_not_expected = ! optimize;
 
   /* Caller save not needed yet.  */
@@ -4209,6 +4509,7 @@ init_function_start (tree subr)
   else
     allocate_struct_function (subr, false);
   prepare_function_start ();
+  decide_function_section (subr);
 
   /* Warn if this value is an aggregate type,
      regardless of which calling convention we are using for it.  */
@@ -4229,8 +4530,8 @@ struct rtl_opt_pass pass_init_function =
  {
   RTL_PASS,
   "*init_function",                     /* name */
-  NULL,                                 /* gate */   
-  init_function_for_compilation,        /* execute */       
+  NULL,                                 /* gate */
+  init_function_for_compilation,        /* execute */
   NULL,                                 /* sub */
   NULL,                                 /* next */
   0,                                    /* static_pass_number */
@@ -4269,12 +4570,8 @@ stack_protect_prologue (void)
   tree guard_decl = targetm.stack_protect_guard ();
   rtx x, y;
 
-  /* Avoid expand_expr here, because we don't want guard_decl pulled
-     into registers unless absolutely necessary.  And we know that
-     crtl->stack_protect_guard is a local stack slot, so this skips
-     all the fluff.  */
-  x = validize_mem (DECL_RTL (crtl->stack_protect_guard));
-  y = validize_mem (DECL_RTL (guard_decl));
+  x = expand_normal (crtl->stack_protect_guard);
+  y = expand_normal (guard_decl);
 
   /* Allow the target to copy from Y to X without leaking Y into a
      register.  */
@@ -4307,12 +4604,8 @@ stack_protect_epilogue (void)
   rtx label = gen_label_rtx ();
   rtx x, y, tmp;
 
-  /* Avoid expand_expr here, because we don't want guard_decl pulled
-     into registers unless absolutely necessary.  And we know that
-     crtl->stack_protect_guard is a local stack slot, so this skips
-     all the fluff.  */
-  x = validize_mem (DECL_RTL (crtl->stack_protect_guard));
-  y = validize_mem (DECL_RTL (guard_decl));
+  x = expand_normal (crtl->stack_protect_guard);
+  y = expand_normal (guard_decl);
 
   /* Allow the target to compare Y with X without leaking either into
      a register.  */
@@ -4487,11 +4780,12 @@ expand_function_start (tree subr)
       if (!DECL_RTL_SET_P (var))
        expand_decl (var);
 
-      t_save = build4 (ARRAY_REF, ptr_type_node,
+      t_save = build4 (ARRAY_REF,
+                      TREE_TYPE (TREE_TYPE (cfun->nonlocal_goto_save_area)),
                       cfun->nonlocal_goto_save_area,
                       integer_zero_node, NULL_TREE, NULL_TREE);
       r_save = expand_expr (t_save, NULL_RTX, VOIDmode, EXPAND_WRITE);
-      r_save = convert_memory_address (Pmode, r_save);
+      gcc_assert (GET_MODE (r_save) == Pmode);
 
       emit_move_insn (r_save, targetm.builtin_setjmp_frame_value ());
       update_nonlocal_goto_save_area ();
@@ -4514,9 +4808,8 @@ expand_function_start (tree subr)
 #endif
     }
 
-  /* After the display initializations is where the stack checking
-     probe should go.  */
-  if(flag_stack_check)
+  /* If we are doing generic stack checking, the probe should go here.  */
+  if (flag_stack_check == GENERIC_STACK_CHECK)
     stack_check_probe_note = emit_note (NOTE_INSN_DELETED);
 
   /* Make sure there is a line number after the function entry setup code.  */
@@ -4611,7 +4904,7 @@ do_warn_unused_parameter (tree fn)
   tree decl;
 
   for (decl = DECL_ARGUMENTS (fn);
-       decl; decl = TREE_CHAIN (decl))
+       decl; decl = DECL_CHAIN (decl))
     if (!TREE_USED (decl) && TREE_CODE (decl) == PARM_DECL
        && DECL_NAME (decl) && !DECL_ARTIFICIAL (decl)
        && !TREE_NO_WARNING (decl))
@@ -4650,6 +4943,7 @@ expand_function_end (void)
              probe_stack_range (STACK_OLD_CHECK_PROTECT, max_frame_size);
            seq = get_insns ();
            end_sequence ();
+           set_insn_locators (seq, prologue_locator);
            emit_insn_before (seq, stack_check_probe_note);
            break;
          }
@@ -4680,7 +4974,7 @@ expand_function_end (void)
   /* Output the label for the actual return from the function.  */
   emit_label (return_label);
 
-  if (USING_SJLJ_EXCEPTIONS)
+  if (targetm_common.except_unwind_info (&global_options) == UI_SJLJ)
     {
       /* Let except.c know where it should emit the call to unregister
         the function context for sjlj exceptions.  */
@@ -4692,7 +4986,7 @@ expand_function_end (void)
       /* We want to ensure that instructions that may trap are not
         moved into the epilogue by scheduling, because we don't
         always emit unwind information for the epilogue.  */
-      if (flag_non_call_exceptions)
+      if (cfun->can_throw_non_call_exceptions)
        emit_insn (gen_blockage ());
     }
 
@@ -4838,7 +5132,8 @@ expand_function_end (void)
   /* @@@ This is a kludge.  We want to ensure that instructions that
      may trap are not moved into the epilogue by scheduling, because
      we don't always emit unwind information for the epilogue.  */
-  if (! USING_SJLJ_EXCEPTIONS && flag_non_call_exceptions)
+  if (cfun->can_throw_non_call_exceptions
+      && targetm_common.except_unwind_info (&global_options) != UI_SJLJ)
     emit_insn (gen_blockage ());
 
   /* If stack protection is enabled for this function, check the guard.  */
@@ -4851,10 +5146,15 @@ expand_function_end (void)
   if (! EXIT_IGNORE_STACK
       && cfun->calls_alloca)
     {
-      rtx tem = 0;
+      rtx tem = 0, seq;
 
-      emit_stack_save (SAVE_FUNCTION, &tem, parm_birth_insn);
-      emit_stack_restore (SAVE_FUNCTION, tem, NULL_RTX);
+      start_sequence ();
+      emit_stack_save (SAVE_FUNCTION, &tem);
+      seq = get_insns ();
+      end_sequence ();
+      emit_insn_before (seq, parm_birth_insn);
+
+      emit_stack_restore (SAVE_FUNCTION, tem);
     }
 
   /* ??? This should no longer be necessary since stupid is no longer with
@@ -4891,6 +5191,8 @@ get_arg_pointer_save_area (void)
       push_topmost_sequence ();
       emit_insn_after (seq, entry_of_function ());
       pop_topmost_sequence ();
+
+      crtl->arg_pointer_save_area_init = true;
     }
 
   return ret;
@@ -4917,19 +5219,25 @@ record_insns (rtx insns, rtx end, htab_t *hashp)
     }
 }
 
-/* INSN has been duplicated as COPY, as part of duping a basic block.
-   If INSN is an epilogue insn, then record COPY as epilogue as well.  */
+/* INSN has been duplicated or replaced by as COPY, perhaps by duplicating a
+   basic block, splitting or peepholes.  If INSN is a prologue or epilogue
+   insn, then record COPY as well.  */
 
 void
-maybe_copy_epilogue_insn (rtx insn, rtx copy)
+maybe_copy_prologue_epilogue_insn (rtx insn, rtx copy)
 {
+  htab_t hash;
   void **slot;
 
-  if (epilogue_insn_hash == NULL
-      || htab_find (epilogue_insn_hash, insn) == NULL)
-    return;
+  hash = epilogue_insn_hash;
+  if (!hash || !htab_find (hash, insn))
+    {
+      hash = prologue_insn_hash;
+      if (!hash || !htab_find (hash, insn))
+       return;
+    }
 
-  slot = htab_find_slot (epilogue_insn_hash, copy, INSERT);
+  slot = htab_find_slot (hash, copy, INSERT);
   gcc_assert (*slot == NULL);
   *slot = copy;
 }
@@ -4978,13 +5286,27 @@ prologue_epilogue_contains (const_rtx insn)
 }
 
 #ifdef HAVE_return
+/* Insert use of return register before the end of BB.  */
+
+static void
+emit_use_return_register_into_block (basic_block bb)
+{
+  rtx seq;
+  start_sequence ();
+  use_return_register ();
+  seq = get_insns ();
+  end_sequence ();
+  emit_insn_before (seq, BB_END (bb));
+}
+
 /* Insert gen_return at the end of block BB.  This also means updating
    block_for_insn appropriately.  */
 
 static void
 emit_return_into_block (basic_block bb)
 {
-  emit_jump_insn_after (gen_return (), BB_END (bb));
+  rtx jump = emit_jump_insn_after (gen_return (), BB_END (bb));
+  JUMP_LABEL (jump) = ret_rtx;
 }
 #endif /* HAVE_return */
 
@@ -4995,17 +5317,45 @@ emit_return_into_block (basic_block bb)
 static void
 thread_prologue_and_epilogue_insns (void)
 {
-  int inserted = 0;
-  edge e;
-#if defined (HAVE_sibcall_epilogue) || defined (HAVE_epilogue) || defined (HAVE_return) || defined (HAVE_prologue)
-  rtx seq;
-#endif
-#if defined (HAVE_epilogue) || defined(HAVE_return)
-  rtx epilogue_end = NULL_RTX;
-#endif
+  bool inserted;
+  rtx seq ATTRIBUTE_UNUSED, epilogue_end ATTRIBUTE_UNUSED;
+  edge entry_edge, e;
   edge_iterator ei;
 
   rtl_profile_for_bb (ENTRY_BLOCK_PTR);
+
+  inserted = false;
+  seq = NULL_RTX;
+  epilogue_end = NULL_RTX;
+
+  /* Can't deal with multiple successors of the entry block at the
+     moment.  Function should always have at least one entry
+     point.  */
+  gcc_assert (single_succ_p (ENTRY_BLOCK_PTR));
+  entry_edge = single_succ_edge (ENTRY_BLOCK_PTR);
+
+  if (flag_split_stack
+      && (lookup_attribute ("no_split_stack", DECL_ATTRIBUTES (cfun->decl))
+         == NULL))
+    {
+#ifndef HAVE_split_stack_prologue
+      gcc_unreachable ();
+#else
+      gcc_assert (HAVE_split_stack_prologue);
+
+      start_sequence ();
+      emit_insn (gen_split_stack_prologue ());
+      seq = get_insns ();
+      end_sequence ();
+
+      record_insns (seq, NULL, &prologue_insn_hash);
+      set_insn_locators (seq, prologue_locator);
+
+      insert_insn_on_edge (seq, entry_edge);
+      inserted = true;
+#endif
+    }
+
 #ifdef HAVE_prologue
   if (HAVE_prologue)
     {
@@ -5013,7 +5363,7 @@ thread_prologue_and_epilogue_insns (void)
       seq = gen_prologue ();
       emit_insn (seq);
 
-      /* Insert an explicit USE for the frame pointer 
+      /* Insert an explicit USE for the frame pointer
          if the profiling is on and the frame pointer is required.  */
       if (crtl->profile && frame_pointer_needed)
        emit_use (hard_frame_pointer_rtx);
@@ -5021,26 +5371,19 @@ thread_prologue_and_epilogue_insns (void)
       /* Retain a map of the prologue insns.  */
       record_insns (seq, NULL, &prologue_insn_hash);
       emit_note (NOTE_INSN_PROLOGUE_END);
-#ifndef PROFILE_BEFORE_PROLOGUE
+
       /* Ensure that instructions are not moved into the prologue when
         profiling is on.  The call to the profiling routine can be
         emitted within the live range of a call-clobbered register.  */
-      if (crtl->profile)
+      if (!targetm.profile_before_prologue () && crtl->profile)
         emit_insn (gen_blockage ());
-#endif
 
       seq = get_insns ();
       end_sequence ();
       set_insn_locators (seq, prologue_locator);
 
-      /* Can't deal with multiple successors of the entry block
-         at the moment.  Function should always have at least one
-         entry point.  */
-      gcc_assert (single_succ_p (ENTRY_BLOCK_PTR));
-
-      insert_insn_on_edge (seq, single_succ_edge (ENTRY_BLOCK_PTR));
-      inserted = 1;
+      insert_insn_on_edge (seq, entry_edge);
+      inserted = true;
     }
 #endif
 
@@ -5065,9 +5408,7 @@ thread_prologue_and_epilogue_insns (void)
       basic_block last;
       rtx label;
 
-      FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
-       if (e->flags & EDGE_FALLTHRU)
-         break;
+      e = find_fallthru_edge (EXIT_BLOCK_PTR->preds);
       if (e == NULL)
        goto epilogue_done;
       last = e->src;
@@ -5107,6 +5448,15 @@ thread_prologue_and_epilogue_insns (void)
                 with a simple return instruction.  */
              if (simplejump_p (jump))
                {
+                 /* The use of the return register might be present in the exit
+                    fallthru block.  Either:
+                    - removing the use is safe, and we should remove the use in
+                      the exit fallthru block, or
+                    - removing the use is not safe, and we should add it here.
+                    For now, we conservatively choose the latter.  Either of the
+                    2 helps in crossjumping.  */
+                 emit_use_return_register_into_block (bb);
+
                  emit_return_into_block (bb);
                  delete_insn (jump);
                }
@@ -5115,12 +5465,15 @@ thread_prologue_and_epilogue_insns (void)
                 that with a conditional return instruction.  */
              else if (condjump_p (jump))
                {
-                 if (! redirect_jump (jump, 0, 0))
+                 if (! redirect_jump (jump, ret_rtx, 0))
                    {
                      ei_next (&ei2);
                      continue;
                    }
 
+                 /* See comment in simple_jump_p case above.  */
+                 emit_use_return_register_into_block (bb);
+
                  /* If this block has only one successor, it both jumps
                     and falls through to the fallthru block, so we can't
                     delete the edge.  */
@@ -5188,29 +5541,44 @@ thread_prologue_and_epilogue_insns (void)
      There really shouldn't be a mixture -- either all should have
      been converted or none, however...  */
 
-  FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
-    if (e->flags & EDGE_FALLTHRU)
-      break;
+  e = find_fallthru_edge (EXIT_BLOCK_PTR->preds);
   if (e == NULL)
     goto epilogue_done;
 
 #ifdef HAVE_epilogue
   if (HAVE_epilogue)
     {
+      rtx returnjump;
+
       start_sequence ();
       epilogue_end = emit_note (NOTE_INSN_EPILOGUE_BEG);
       seq = gen_epilogue ();
-      emit_jump_insn (seq);
+      if (seq)
+       emit_jump_insn (seq);
 
       /* Retain a map of the epilogue insns.  */
       record_insns (seq, NULL, &epilogue_insn_hash);
       set_insn_locators (seq, epilogue_locator);
 
+      returnjump = get_last_insn ();
       seq = get_insns ();
       end_sequence ();
 
       insert_insn_on_edge (seq, e);
-      inserted = 1;
+      inserted = true;
+
+      if (JUMP_P (returnjump))
+       {
+         rtx pat = PATTERN (returnjump);
+         if (GET_CODE (pat) == PARALLEL)
+           pat = XVECEXP (pat, 0, 0);
+         if (ANY_RETURN_P (pat))
+           JUMP_LABEL (returnjump) = pat;
+         else
+           JUMP_LABEL (returnjump) = ret_rtx;
+       }
+      else
+       returnjump = NULL_RTX;
     }
   else
 #endif
@@ -5233,13 +5601,23 @@ thread_prologue_and_epilogue_insns (void)
          cur_bb->aux = cur_bb->next_bb;
       cfg_layout_finalize ();
     }
+
 epilogue_done:
   default_rtl_profile ();
 
   if (inserted)
     {
+      sbitmap blocks;
+
       commit_edge_insertions ();
 
+      /* Look for basic blocks within the prologue insns.  */
+      blocks = sbitmap_alloc (last_basic_block);
+      sbitmap_zero (blocks);
+      SET_BIT (blocks, entry_edge->dest->index);
+      find_many_sub_basic_blocks (blocks);
+      sbitmap_free (blocks);
+
       /* The epilogue insns we inserted may cause the exit edge to no longer
         be fallthru.  */
       FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
@@ -5294,7 +5672,7 @@ epilogue_done:
       for (insn = epilogue_end; insn; insn = next)
        {
          next = NEXT_INSN (insn);
-         if (NOTE_P (insn) 
+         if (NOTE_P (insn)
              && (NOTE_KIND (insn) == NOTE_INSN_FUNCTION_BEG))
            reorder_insns (insn, insn, PREV_INSN (epilogue_end));
        }
@@ -5397,7 +5775,7 @@ reposition_prologue_and_epilogue_notes (void)
          if (note)
            {
              /* If the function has a single basic block, and no real
-                epilogue insns (e.g. sibcall with no cleanup), the 
+                epilogue insns (e.g. sibcall with no cleanup), the
                 epilogue note can get scheduled before the prologue
                 note.  If we have frame related prologue insns, having
                 them scanned during the epilogue will result in a crash.
@@ -5457,8 +5835,15 @@ void
 used_types_insert (tree t)
 {
   while (POINTER_TYPE_P (t) || TREE_CODE (t) == ARRAY_TYPE)
-    t = TREE_TYPE (t);
-  t = TYPE_MAIN_VARIANT (t);
+    if (TYPE_NAME (t))
+      break;
+    else
+      t = TREE_TYPE (t);
+  if (TREE_CODE (t) == ERROR_MARK)
+    return;
+  if (TYPE_NAME (t) == NULL_TREE
+      || TYPE_NAME (t) == TYPE_NAME (TYPE_MAIN_VARIANT (t)))
+    t = TYPE_MAIN_VARIANT (t);
   if (debug_info_level > DINFO_LEVEL_NONE)
     {
       if (cfun)
@@ -5467,9 +5852,7 @@ used_types_insert (tree t)
        /* So this might be a type referenced by a global variable.
           Record that type so that we can later decide to emit its debug
           information.  */
-       types_used_by_cur_var_decl =
-         tree_cons (t, NULL, types_used_by_cur_var_decl);
-
+        VEC_safe_push (tree, gc, types_used_by_cur_var_decl, t);
     }
 }
 
@@ -5528,8 +5911,7 @@ types_used_by_var_decl_insert (tree type, tree var_decl)
       if (*slot == NULL)
        {
          struct types_used_by_vars_entry *entry;
-         entry = (struct types_used_by_vars_entry*) ggc_alloc
-                   (sizeof (struct types_used_by_vars_entry));
+         entry = ggc_alloc_types_used_by_vars_entry ();
          entry->type = type;
          entry->var_decl = var_decl;
          *slot = entry;
@@ -5561,12 +5943,17 @@ rest_of_handle_thread_prologue_and_epilogue (void)
 {
   if (optimize)
     cleanup_cfg (CLEANUP_EXPENSIVE);
+
   /* On some machines, the prologue and epilogue code, or parts thereof,
      can be represented as RTL.  Doing so lets us schedule insns between
      it and the rest of the code and also allows delayed branch
      scheduling to operate in the epilogue.  */
-
   thread_prologue_and_epilogue_insns ();
+
+  /* The stack usage info is finalized during prologue expansion.  */
+  if (flag_stack_usage_info)
+    output_stack_usage ();
+
   return 0;
 }
 
@@ -5585,7 +5972,6 @@ struct rtl_opt_pass pass_thread_prologue_and_epilogue =
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   TODO_verify_flow,                     /* todo_flags_start */
-  TODO_dump_func |
   TODO_df_verify |
   TODO_df_finish | TODO_verify_rtl_sharing |
   TODO_ggc_collect                      /* todo_flags_finish */
@@ -5594,7 +5980,7 @@ struct rtl_opt_pass pass_thread_prologue_and_epilogue =
 \f
 
 /* This mini-pass fixes fall-out from SSA in asm statements that have
-   in-out constraints.  Say you start with 
+   in-out constraints.  Say you start with
 
      orig = inout;
      asm ("": "+mr" (inout));
@@ -5787,7 +6173,7 @@ struct rtl_opt_pass pass_match_asm_constraints =
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_func                       /* todo_flags_finish */
+  0                                     /* todo_flags_finish */
  }
 };