OSDN Git Service

(fixup_var_refs_1): Fix error in last change (when mode of VAR is not
[pf3gnuchains/gcc-fork.git] / gcc / function.c
index 455395b..b62a284 100644 (file)
@@ -1,5 +1,5 @@
 /* Expands front end tree to back end RTL for GNU C-Compiler
-   Copyright (C) 1987, 88, 89, 91, 92, 1993 Free Software Foundation, Inc.
+   Copyright (C) 1987, 88, 89, 91-94, 1995 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.  */
 
 
 /* This file handles the generation of rtl code from tree structure
@@ -60,7 +61,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 /* Some systems use __main in a way incompatible with its use in gcc, in these
    cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to
    give the same symbol without quotes for an alternative entry point.  You
-   must define both, or niether. */
+   must define both, or neither. */
 #ifndef NAME__MAIN
 #define NAME__MAIN "__main"
 #define SYMBOL__MAIN __main
@@ -152,9 +153,8 @@ int current_function_args_size;
 
 int current_function_pretend_args_size;
 
-/* # of bytes of outgoing arguments required to be pushed by the prologue.
-   If this is non-zero, it means that ACCUMULATE_OUTGOING_ARGS was defined
-   and no stack adjusts will be done on function calls.  */
+/* # of bytes of outgoing arguments.  If ACCUMULATE_OUTGOING_ARGS is
+   defined, the needed space is pushed by the prologue. */
 
 int current_function_outgoing_args_size;
 
@@ -168,6 +168,11 @@ rtx current_function_arg_offset_rtx;
 
 int current_function_varargs;
 
+/* Nonzero if current function uses stdarg.h or equivalent.
+   Zero for functions that use varargs.h.  */
+
+int current_function_stdarg;
+
 /* Quantities of various kinds of registers
    used for the current function's args.  */
 
@@ -306,32 +311,22 @@ static tree empty_cleanup_list;
    assign_stack_local uses frame_pointer_rtx when this is nonzero.  */
 static int virtuals_instantiated;
 
+/* These variables hold pointers to functions to
+   save and restore machine-specific data,
+   in push_function_context and pop_function_context.  */
+void (*save_machine_status) ();
+void (*restore_machine_status) ();
+
 /* Nonzero if we need to distinguish between the return value of this function
    and the return value of a function called by this function.  This helps
    integrate.c  */
 
 extern int rtx_equal_function_value_matters;
+extern tree sequence_rtl_expr;
 extern tree bc_runtime_type_code ();
 extern rtx bc_build_calldesc ();
 extern char *bc_emit_trampoline ();
 extern char *bc_end_function ();
-
-void fixup_gotos ();
-
-static tree round_down ();
-static rtx round_trampoline_addr ();
-static rtx fixup_stack_1 ();
-static void put_reg_into_stack ();
-static void fixup_var_refs ();
-static void fixup_var_refs_insns ();
-static void fixup_var_refs_1 ();
-static void optimize_bit_field ();
-static void instantiate_decls ();
-static void instantiate_decls_1 ();
-static void instantiate_decl ();
-static int instantiate_virtual_regs_1 ();
-static rtx fixup_memory_subreg ();
-static rtx walk_fixup_memory_subreg ();
 \f
 /* In order to evaluate some expressions, such as function calls returning
    structures in memory, we need to temporarily allocate stack locations.
@@ -357,14 +352,27 @@ struct temp_slot
   struct temp_slot *next;
   /* The rtx to used to reference the slot. */
   rtx slot;
+  /* The rtx used to represent the address if not the address of the
+     slot above.  May be an EXPR_LIST if multiple addresses exist.  */
+  rtx address;
   /* The size, in units, of the slot.  */
   int size;
+  /* The value of `sequence_rtl_expr' when this temporary is allocated.  */
+  tree rtl_expr;
   /* Non-zero if this temporary is currently in use.  */
   char in_use;
+  /* Non-zero if this temporary has its address taken.  */
+  char addr_taken;
   /* Nesting level at which this slot is being used.  */
   int level;
   /* Non-zero if this should survive a call to free_temp_slots.  */
   int keep;
+  /* The offset of the slot from the frame_pointer, including extra space
+     for alignment.  This info is for combine_temp_slots.  */
+  int base_offset;
+  /* The size of the slot, including extra space for alignment.  This
+     info is for combine_temp_slots.  */
+  int full_size;
 };
 
 /* List of all temporaries allocated, both available and in use.  */
@@ -399,6 +407,50 @@ int max_stack_depth;
 
 /* Current depth in statement expressions.  */
 static int stmt_expr_depth;
+
+/* This structure is used to record MEMs or pseudos used to replace VAR, any
+   SUBREGs of VAR, and any MEMs containing VAR as an address.  We need to
+   maintain this list in case two operands of an insn were required to match;
+   in that case we must ensure we use the same replacement.  */
+
+struct fixup_replacement
+{
+  rtx old;
+  rtx new;
+  struct fixup_replacement *next;
+};
+   
+/* Forward declarations.  */
+
+static struct temp_slot *find_temp_slot_from_address  PROTO((rtx));
+static void put_reg_into_stack PROTO((struct function *, rtx, tree,
+                                      enum machine_mode, enum machine_mode,
+                                      int));
+static void fixup_var_refs     PROTO((rtx, enum machine_mode, int));
+static struct fixup_replacement
+  *find_fixup_replacement      PROTO((struct fixup_replacement **, rtx));
+static void fixup_var_refs_insns PROTO((rtx, enum machine_mode, int,
+                                       rtx, int));
+static void fixup_var_refs_1   PROTO((rtx, enum machine_mode, rtx *, rtx,
+                                      struct fixup_replacement **));
+static rtx fixup_memory_subreg PROTO((rtx, rtx, int));
+static rtx walk_fixup_memory_subreg  PROTO((rtx, rtx, int));
+static rtx fixup_stack_1       PROTO((rtx, rtx));
+static void optimize_bit_field PROTO((rtx, rtx, rtx *));
+static void instantiate_decls  PROTO((tree, int));
+static void instantiate_decls_1        PROTO((tree, int));
+static void instantiate_decl   PROTO((rtx, int, int));
+static int instantiate_virtual_regs_1 PROTO((rtx *, rtx, int));
+static void delete_handlers    PROTO((void));
+static void pad_to_arg_alignment PROTO((struct args_size *, int));
+static void pad_below          PROTO((struct args_size *, enum  machine_mode,
+                                      tree));
+static tree round_down         PROTO((tree, int));
+static rtx round_trampoline_addr PROTO((rtx));
+static tree blocks_nreverse    PROTO((tree));
+static int all_blocks          PROTO((tree, tree *));
+static int *record_insns       PROTO((rtx));
+static int contains            PROTO((rtx, int *));
 \f
 /* Pointer to chain of `struct function' for containing functions.  */
 struct function *outer_function_chain;
@@ -423,7 +475,8 @@ find_function_data (decl)
    since this function knows only about language-independent variables.  */
 
 void
-push_function_context ()
+push_function_context_to (context)
+     tree context;
 {
   struct function *p = (struct function *) xmalloc (sizeof (struct function));
 
@@ -441,9 +494,12 @@ push_function_context ()
   p->calls_alloca = current_function_calls_alloca;
   p->has_nonlocal_label = current_function_has_nonlocal_label;
   p->has_nonlocal_goto = current_function_has_nonlocal_goto;
+  p->contains_functions = current_function_contains_functions;
   p->args_size = current_function_args_size;
   p->pretend_args_size = current_function_pretend_args_size;
   p->arg_offset_rtx = current_function_arg_offset_rtx;
+  p->varargs = current_function_varargs;
+  p->stdarg = current_function_stdarg;
   p->uses_const_pool = current_function_uses_const_pool;
   p->uses_pic_offset_table = current_function_uses_pic_offset_table;
   p->internal_arg_pointer = current_function_internal_arg_pointer;
@@ -473,25 +529,38 @@ push_function_context ()
   p->fixup_var_refs_queue = 0;
   p->epilogue_delay_list = current_function_epilogue_delay_list;
 
-  save_tree_status (p);
+  save_tree_status (p, context);
   save_storage_status (p);
   save_emit_status (p);
   init_emit ();
   save_expr_status (p);
   save_stmt_status (p);
   save_varasm_status (p);
+
+  if (save_machine_status)
+    (*save_machine_status) (p);
+}
+
+void
+push_function_context ()
+{
+  push_function_context_to (current_function_decl);
 }
 
 /* Restore the last saved context, at the end of a nested function.
    This function is called from language-specific code.  */
 
 void
-pop_function_context ()
+pop_function_context_from (context)
+     tree context;
 {
   struct function *p = outer_function_chain;
 
   outer_function_chain = p->next;
 
+  current_function_contains_functions
+    = p->contains_functions || p->inline_obstacks
+      || context == current_function_decl;
   current_function_name = p->name;
   current_function_decl = p->decl;
   current_function_pops_args = p->pops_args;
@@ -503,10 +572,11 @@ pop_function_context ()
   current_function_calls_alloca = p->calls_alloca;
   current_function_has_nonlocal_label = p->has_nonlocal_label;
   current_function_has_nonlocal_goto = p->has_nonlocal_goto;
-  current_function_contains_functions = 1;
   current_function_args_size = p->args_size;
   current_function_pretend_args_size = p->pretend_args_size;
   current_function_arg_offset_rtx = p->arg_offset_rtx;
+  current_function_varargs = p->varargs;
+  current_function_stdarg = p->stdarg;
   current_function_uses_const_pool = p->uses_const_pool;
   current_function_uses_pic_offset_table = p->uses_pic_offset_table;
   current_function_internal_arg_pointer = p->internal_arg_pointer;
@@ -534,6 +604,7 @@ pop_function_context ()
   temp_slots = p->temp_slots;
   temp_slot_level = p->temp_slot_level;
   current_function_epilogue_delay_list = p->epilogue_delay_list;
+  reg_renumber = 0;
 
   restore_tree_status (p);
   restore_storage_status (p);
@@ -542,6 +613,9 @@ pop_function_context ()
   restore_stmt_status (p);
   restore_varasm_status (p);
 
+  if (restore_machine_status)
+    (*restore_machine_status) (p);
+
   /* Finish doing put_var_into_stack for any of our variables
      which became addressable during the nested function.  */
   {
@@ -556,6 +630,11 @@ pop_function_context ()
   rtx_equal_function_value_matters = 1;
   virtuals_instantiated = 0;
 }
+
+void pop_function_context ()
+{
+  pop_function_context_from (current_function_decl);
+}
 \f
 /* Allocate fixed slots in the stack frame of the current function.  */
 
@@ -620,10 +699,8 @@ assign_stack_local (mode, size, align)
 
   /* 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
-  if (mode != BLKmode)
+  if (BYTES_BIG_ENDIAN && mode != BLKmode)
     bigend_correction = size - GET_MODE_SIZE (mode);
-#endif
 
 #ifdef FRAME_GROWS_DOWNWARD
   frame_offset -= size;
@@ -693,10 +770,8 @@ assign_outer_stack_local (mode, size, align, function)
 
   /* 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
-  if (mode != BLKmode)
+  if (BYTES_BIG_ENDIAN && mode != BLKmode)
     bigend_correction = size - GET_MODE_SIZE (mode);
-#endif
 
 #ifdef FRAME_GROWS_DOWNWARD
   function->frame_offset -= size;
@@ -725,9 +800,10 @@ assign_outer_stack_local (mode, size, align, function)
    SIZE is the size in units of the space required.  We do no rounding here
    since assign_stack_local will do any required rounding.
 
-   KEEP is non-zero if this slot is to be retained after a call to
-   free_temp_slots.  Automatic variables for a block are allocated with this
-   flag.  */
+   KEEP is 1 if this slot is to be retained after a call to
+   free_temp_slots.  Automatic variables for a block are allocated
+   with this flag.  KEEP is 2, if we allocate a longer term temporary,
+   whose lifetime is controlled by CLEANUP_POINT_EXPRs.  */
 
 rtx
 assign_stack_temp (mode, size, keep)
@@ -737,6 +813,11 @@ assign_stack_temp (mode, size, keep)
 {
   struct temp_slot *p, *best_p = 0;
 
+  /* If SIZE is -1 it means that somebody tried to allocate a temporary
+     of a variable size.  */
+  if (size == -1)
+    abort ();
+
   /* First try to find an available, already-allocated temporary that is the
      exact size we require.  */
   for (p = temp_slots; p; p = p->next)
@@ -765,11 +846,15 @@ assign_stack_temp (mode, size, keep)
          if (best_p->size - rounded_size >= alignment)
            {
              p = (struct temp_slot *) oballoc (sizeof (struct temp_slot));
-             p->in_use = 0;
+             p->in_use = p->addr_taken = 0;
              p->size = best_p->size - rounded_size;
+             p->base_offset = best_p->base_offset + rounded_size;
+             p->full_size = best_p->full_size - rounded_size;
              p->slot = gen_rtx (MEM, BLKmode,
                                 plus_constant (XEXP (best_p->slot, 0),
                                                rounded_size));
+             p->address = 0;
+             p->rtl_expr = 0;
              p->next = temp_slots;
              temp_slots = p;
 
@@ -777,28 +862,60 @@ assign_stack_temp (mode, size, keep)
                                         stack_slot_list);
 
              best_p->size = rounded_size;
+             best_p->full_size = rounded_size;
            }
        }
 
       p = best_p;
     }
              
-
   /* If we still didn't find one, make a new temporary.  */
   if (p == 0)
     {
+      int frame_offset_old = frame_offset;
       p = (struct temp_slot *) oballoc (sizeof (struct temp_slot));
-      p->size = size;
       /* If the temp slot mode doesn't indicate the alignment,
         use the largest possible, so no one will be disappointed.  */
-      p->slot = assign_stack_local (mode, size, mode == BLKmode ? -1 : 0); 
+      p->slot = assign_stack_local (mode, size, mode == BLKmode ? -1 : 0);
+      /* The following slot size computation is necessary because we don't
+        know the actual size of the temporary slot until assign_stack_local
+        has performed all the frame alignment and size rounding for the
+        requested temporary.  Note that extra space added for alignment
+        can be either above or below this stack slot depending on which
+        way the frame grows.  We include the extra space if and only if it
+        is above this slot.  */
+#ifdef FRAME_GROWS_DOWNWARD
+      p->size = frame_offset_old - frame_offset;
+#else
+      p->size = size;
+#endif
+      /* Now define the fields used by combine_temp_slots.  */
+#ifdef FRAME_GROWS_DOWNWARD
+      p->base_offset = frame_offset;
+      p->full_size = frame_offset_old - frame_offset;
+#else
+      p->base_offset = frame_offset_old;
+      p->full_size = frame_offset - frame_offset_old;
+#endif
+      p->address = 0;
       p->next = temp_slots;
       temp_slots = p;
     }
 
   p->in_use = 1;
-  p->level = temp_slot_level;
-  p->keep = keep;
+  p->addr_taken = 0;
+  p->rtl_expr = sequence_rtl_expr;
+
+  if (keep == 2)
+    {
+      p->level = target_temp_slot_level;
+      p->keep = 0;
+    }
+  else
+    {
+      p->level = temp_slot_level;
+      p->keep = keep;
+    }
   return p->slot;
 }
 
@@ -825,18 +942,18 @@ combine_temp_slots ()
            int delete_q = 0;
            if (! q->in_use && GET_MODE (q->slot) == BLKmode)
              {
-               if (rtx_equal_p (plus_constant (XEXP (p->slot, 0), p->size),
-                                XEXP (q->slot, 0)))
+               if (p->base_offset + p->full_size == q->base_offset)
                  {
                    /* Q comes after P; combine Q into P.  */
                    p->size += q->size;
+                   p->full_size += q->full_size;
                    delete_q = 1;
                  }
-               else if (rtx_equal_p (plus_constant (XEXP (q->slot, 0), q->size),
-                                     XEXP (p->slot, 0)))
+               else if (q->base_offset + q->full_size == p->base_offset)
                  {
                    /* P comes after Q; combine P into Q.  */
                    q->size += p->size;
+                   q->full_size += p->full_size;
                    delete_p = 1;
                    break;
                  }
@@ -863,6 +980,77 @@ combine_temp_slots ()
   rtx_free (free_pointer);
 }
 \f
+/* Find the temp slot corresponding to the object at address X.  */
+
+static struct temp_slot *
+find_temp_slot_from_address (x)
+     rtx x;
+{
+  struct temp_slot *p;
+  rtx next;
+
+  for (p = temp_slots; p; p = p->next)
+    {
+      if (! p->in_use)
+       continue;
+      else if (XEXP (p->slot, 0) == x
+              || p->address == x)
+       return p;
+
+      else if (p->address != 0 && GET_CODE (p->address) == EXPR_LIST)
+       for (next = p->address; next; next = XEXP (next, 1))
+         if (XEXP (next, 0) == x)
+           return p;
+    }
+
+  return 0;
+}
+      
+/* Indicate that NEW is an alternate way of referring to the temp slot
+   that previous was known by OLD.  */
+
+void
+update_temp_slot_address (old, new)
+     rtx old, new;
+{
+  struct temp_slot *p = find_temp_slot_from_address (old);
+
+  /* If none, return.  Else add NEW as an alias.  */
+  if (p == 0)
+    return;
+  else if (p->address == 0)
+    p->address = new;
+  else
+    {
+      if (GET_CODE (p->address) != EXPR_LIST)
+       p->address = gen_rtx (EXPR_LIST, VOIDmode, p->address, NULL_RTX);
+
+      p->address = gen_rtx (EXPR_LIST, VOIDmode, new, p->address);
+    }
+}
+
+/* If X could be a reference to a temporary slot, mark the fact that its
+   address was taken.  */
+
+void
+mark_temp_addr_taken (x)
+     rtx x;
+{
+  struct temp_slot *p;
+
+  if (x == 0)
+    return;
+
+  /* If X is not in memory or is at a constant address, it cannot be in
+     a temporary slot.  */
+  if (GET_CODE (x) != MEM || CONSTANT_P (XEXP (x, 0)))
+    return;
+
+  p = find_temp_slot_from_address (XEXP (x, 0));
+  if (p != 0)
+    p->addr_taken = 1;
+}
+
 /* If X could be a reference to a temporary slot, mark that slot as belonging
    to the to one level higher.  If X matched one of our slots, just mark that
    one.  Otherwise, we can't easily predict which it is, so upgrade all of
@@ -875,20 +1063,56 @@ void
 preserve_temp_slots (x)
      rtx x;
 {
-  struct temp_slot *p;
+  struct temp_slot *p = 0;
+
+  /* If there is no result, we still might have some objects whose address
+     were taken, so we need to make sure they stay around.  */
+  if (x == 0)
+    {
+      for (p = temp_slots; p; p = p->next)
+       if (p->in_use && p->level == temp_slot_level && p->addr_taken)
+         p->level--;
+
+      return;
+    }
+
+  /* If X is a register that is being used as a pointer, see if we have
+     a temporary slot we know it points to.  To be consistent with
+     the code below, we really should preserve all non-kept slots
+     if we can't find a match, but that seems to be much too costly.  */
+  if (GET_CODE (x) == REG && REGNO_POINTER_FLAG (REGNO (x)))
+    p = find_temp_slot_from_address (x);
 
   /* If X is not in memory or is at a constant address, it cannot be in
-     a temporary slot.  */
-  if (x == 0 || GET_CODE (x) != MEM || CONSTANT_P (XEXP (x, 0)))
-    return;
+     a temporary slot, but it can contain something whose address was
+     taken.  */
+  if (p == 0 && (GET_CODE (x) != MEM || CONSTANT_P (XEXP (x, 0))))
+    {
+      for (p = temp_slots; p; p = p->next)
+       if (p->in_use && p->level == temp_slot_level && p->addr_taken)
+         p->level--;
+
+      return;
+    }
 
   /* First see if we can find a match.  */
-  for (p = temp_slots; p; p = p->next)
-    if (p->in_use && x == p->slot)
-      {
-       p->level--;
-       return;
-      }
+  if (p == 0)
+    p = find_temp_slot_from_address (XEXP (x, 0));
+
+  if (p != 0)
+    {
+      /* Move everything at our level whose address was taken to our new
+        level in case we used its address.  */
+      struct temp_slot *q;
+
+      for (q = temp_slots; q; q = q->next)
+       if (q != p && q->addr_taken && q->level == p->level)
+         q->level--;
+
+      p->level--;
+      p->addr_taken = 0;
+      return;
+    }
 
   /* Otherwise, preserve all non-kept slots at this level.  */
   for (p = temp_slots; p; p = p->next)
@@ -896,8 +1120,40 @@ preserve_temp_slots (x)
       p->level--;
 }
 
+/* X is the result of an RTL_EXPR.  If it is a temporary slot associated
+   with that RTL_EXPR, promote it into a temporary slot at the present
+   level so it will not be freed when we free slots made in the
+   RTL_EXPR.  */
+
+void
+preserve_rtl_expr_result (x)
+     rtx x;
+{
+  struct temp_slot *p;
+
+  /* If X is not in memory or is at a constant address, it cannot be in
+     a temporary slot.  */
+  if (x == 0 || GET_CODE (x) != MEM || CONSTANT_P (XEXP (x, 0)))
+    return;
+
+  /* If we can find a match, move it to our level unless it is already at
+     an upper level.  */
+  p = find_temp_slot_from_address (XEXP (x, 0));
+  if (p != 0)
+    {
+      p->level = MIN (p->level, temp_slot_level);
+      p->rtl_expr = 0;
+    }
+
+  return;
+}
+
 /* Free all temporaries used so far.  This is normally called at the end
-   of generating code for a statement.  */
+   of generating code for a statement.  Don't free any temporaries
+   currently in use for an RTL_EXPR that hasn't yet been emitted.
+   We could eventually do better than this since it can be reused while
+   generating the same RTL_EXPR, but this is complex and probably not
+   worthwhile.  */
 
 void
 free_temp_slots ()
@@ -905,7 +1161,23 @@ free_temp_slots ()
   struct temp_slot *p;
 
   for (p = temp_slots; p; p = p->next)
-    if (p->in_use && p->level == temp_slot_level && ! p->keep)
+    if (p->in_use && p->level == temp_slot_level && ! p->keep
+       && p->rtl_expr == 0)
+      p->in_use = 0;
+
+  combine_temp_slots ();
+}
+
+/* Free all temporary slots used in T, an RTL_EXPR node.  */
+
+void
+free_temps_for_rtl_expr (t)
+     tree t;
+{
+  struct temp_slot *p;
+
+  for (p = temp_slots; p; p = p->next)
+    if (p->rtl_expr == t)
       p->in_use = 0;
 
   combine_temp_slots ();
@@ -916,14 +1188,6 @@ free_temp_slots ()
 void
 push_temp_slots ()
 {
-  /* For GNU C++, we must allow a sequence to be emitted anywhere in
-     the level where the sequence was started.  By not changing levels
-     when the compiler is inside a sequence, the temporaries for the
-     sequence and the temporaries will not unwittingly conflict with
-     the temporaries for other sequences and/or code at that level.  */
-  if (in_sequence_p ())
-    return;
-
   temp_slot_level++;
 }
 
@@ -935,19 +1199,25 @@ pop_temp_slots ()
 {
   struct temp_slot *p;
 
-  /* See comment in push_temp_slots about why we don't change levels
-     in sequences.  */
-  if (in_sequence_p ())
-    return;
-
   for (p = temp_slots; p; p = p->next)
-    if (p->in_use && p->level == temp_slot_level)
+    if (p->in_use && p->level == temp_slot_level && p->rtl_expr == 0)
       p->in_use = 0;
 
   combine_temp_slots ();
 
   temp_slot_level--;
 }
+
+/* Initialize temporary slots.  */
+
+void
+init_temp_slots ()
+{
+  /* We have not allocated any temporaries yet.  */
+  temp_slots = 0;
+  temp_slot_level = 0;
+  target_temp_slot_level = 0;
+}
 \f
 /* Retroactively move an auto variable from a register to a stack slot.
    This is done when an address-reference to the variable is seen.  */
@@ -1003,28 +1273,30 @@ put_var_into_stack (decl)
 
   if (GET_CODE (reg) == REG)
     put_reg_into_stack (function, reg, TREE_TYPE (decl),
-                       promoted_mode, decl_mode);
+                       promoted_mode, decl_mode, TREE_SIDE_EFFECTS (decl));
   else if (GET_CODE (reg) == CONCAT)
     {
       /* A CONCAT contains two pseudos; put them both in the stack.
         We do it so they end up consecutive.  */
       enum machine_mode part_mode = GET_MODE (XEXP (reg, 0));
       tree part_type = TREE_TYPE (TREE_TYPE (decl));
-#ifdef STACK_GROWS_DOWNWARD
+#ifdef FRAME_GROWS_DOWNWARD
       /* Since part 0 should have a lower address, do it second.  */
-      put_reg_into_stack (function, XEXP (reg, 1),
-                         part_type, part_mode, part_mode);
-      put_reg_into_stack (function, XEXP (reg, 0),
-                         part_type, part_mode, part_mode);
+      put_reg_into_stack (function, XEXP (reg, 1), part_type, part_mode,
+                         part_mode, TREE_SIDE_EFFECTS (decl));
+      put_reg_into_stack (function, XEXP (reg, 0), part_type, part_mode,
+                         part_mode, TREE_SIDE_EFFECTS (decl));
 #else
-      put_reg_into_stack (function, XEXP (reg, 0),
-                         part_type, part_mode, part_mode);
-      put_reg_into_stack (function, XEXP (reg, 1),
-                         part_type, part_mode, part_mode);
+      put_reg_into_stack (function, XEXP (reg, 0), part_type, part_mode,
+                         part_mode, TREE_SIDE_EFFECTS (decl));
+      put_reg_into_stack (function, XEXP (reg, 1), part_type, part_mode,
+                         part_mode, TREE_SIDE_EFFECTS (decl));
 #endif
 
       /* Change the CONCAT into a combined MEM for both parts.  */
       PUT_CODE (reg, MEM);
+      MEM_VOLATILE_P (reg) = MEM_VOLATILE_P (XEXP (reg, 0));
+
       /* The two parts are in memory order already.
         Use the lower parts address as ours.  */
       XEXP (reg, 0) = XEXP (XEXP (reg, 0), 0);
@@ -1037,14 +1309,16 @@ put_var_into_stack (decl)
 /* Subroutine of put_var_into_stack.  This puts a single pseudo reg REG
    into the stack frame of FUNCTION (0 means the current function).
    DECL_MODE is the machine mode of the user-level data type.
-   PROMOTED_MODE is the machine mode of the register.  */
+   PROMOTED_MODE is the machine mode of the register.
+   VOLATILE_P is nonzero if this is for a "volatile" decl.  */
 
 static void
-put_reg_into_stack (function, reg, type, promoted_mode, decl_mode)
+put_reg_into_stack (function, reg, type, promoted_mode, decl_mode, volatile_p)
      struct function *function;
      rtx reg;
      tree type;
      enum machine_mode promoted_mode, decl_mode;
+     int volatile_p;
 {
   rtx new = 0;
 
@@ -1064,19 +1338,15 @@ put_reg_into_stack (function, reg, type, promoted_mode, decl_mode)
        new = assign_stack_local (decl_mode, GET_MODE_SIZE (decl_mode), 0);
     }
 
+  PUT_MODE (reg, decl_mode);
   XEXP (reg, 0) = XEXP (new, 0);
   /* `volatil' bit means one thing for MEMs, another entirely for REGs.  */
-  REG_USERVAR_P (reg) = 0;
+  MEM_VOLATILE_P (reg) = volatile_p;
   PUT_CODE (reg, MEM);
-  PUT_MODE (reg, decl_mode);
 
   /* If this is a memory ref that contains aggregate components,
      mark it as such for cse and loop optimize.  */
-  MEM_IN_STRUCT_P (reg)
-    = (TREE_CODE (type) == ARRAY_TYPE
-       || TREE_CODE (type) == RECORD_TYPE
-       || TREE_CODE (type) == UNION_TYPE
-       || TREE_CODE (type) == QUAL_UNION_TYPE);
+  MEM_IN_STRUCT_P (reg) = AGGREGATE_TYPE_P (type);
 
   /* Now make sure that all refs to the variable, previously made
      when it was a register, are fixed up to be valid again.  */
@@ -1087,8 +1357,11 @@ put_reg_into_stack (function, reg, type, promoted_mode, decl_mode)
       /* Variable is inherited; fix it up when we get back to its function.  */
       push_obstacks (function->function_obstack,
                     function->function_maybepermanent_obstack);
+
+      /* See comment in restore_tree_status in tree.c for why this needs to be
+        on saveable obstack.  */
       temp
-       = (struct var_refs_queue *) oballoc (sizeof (struct var_refs_queue));
+       = (struct var_refs_queue *) savealloc (sizeof (struct var_refs_queue));
       temp->modified = reg;
       temp->promoted_mode = promoted_mode;
       temp->unsignedp = TREE_UNSIGNED (type);
@@ -1140,20 +1413,7 @@ fixup_var_refs (var, promoted_mode, unsignedp)
     }
 }
 \f
-/* This structure is used by the following two functions to record MEMs or
-   pseudos used to replace VAR, any SUBREGs of VAR, and any MEMs containing
-   VAR as an address.  We need to maintain this list in case two operands of
-   an insn were required to match; in that case we must ensure we use the
-   same replacement.  */
-
-struct fixup_replacement
-{
-  rtx old;
-  rtx new;
-  struct fixup_replacement *next;
-};
-   
-/* REPLACEMENTS is a pointer to a list of the above structures and X is
+/* REPLACEMENTS is a pointer to a list of the struct fixup_replacement and X is
    some part of an insn.  Return a struct fixup_replacement whose OLD
    value is equal to X.  Allocate a new structure if no such entry exists. */
 
@@ -1200,15 +1460,37 @@ fixup_var_refs_insns (var, promoted_mode, unsignedp, insn, toplevel)
       rtx note;
       if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
        {
+         /* If this is a CLOBBER of VAR, delete it.
+
+            If it has a REG_LIBCALL note, delete the REG_LIBCALL
+            and REG_RETVAL notes too.  */
+         if (GET_CODE (PATTERN (insn)) == CLOBBER
+             && XEXP (PATTERN (insn), 0) == var)
+           {
+             if ((note = find_reg_note (insn, REG_LIBCALL, NULL_RTX)) != 0)
+               /* The REG_LIBCALL note will go away since we are going to
+                  turn INSN into a NOTE, so just delete the
+                  corresponding REG_RETVAL note.  */
+               remove_note (XEXP (note, 0),
+                            find_reg_note (XEXP (note, 0), REG_RETVAL,
+                                           NULL_RTX));
+
+             /* In unoptimized compilation, we shouldn't call delete_insn
+                except in jump.c doing warnings.  */
+             PUT_CODE (insn, NOTE);
+             NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+             NOTE_SOURCE_FILE (insn) = 0;
+           }
+
          /* The insn to load VAR from a home in the arglist
             is now a no-op.  When we see it, just delete it.  */
-         if (toplevel
-             && GET_CODE (PATTERN (insn)) == SET
-             && SET_DEST (PATTERN (insn)) == var
-             /* If this represents the result of an insn group,
-                don't delete the insn.  */
-             && find_reg_note (insn, REG_RETVAL, NULL_RTX) == 0
-             && rtx_equal_p (SET_SRC (PATTERN (insn)), var))
+         else if (toplevel
+                  && GET_CODE (PATTERN (insn)) == SET
+                  && SET_DEST (PATTERN (insn)) == var
+                  /* If this represents the result of an insn group,
+                     don't delete the insn.  */
+                  && find_reg_note (insn, REG_RETVAL, NULL_RTX) == 0
+                  && rtx_equal_p (SET_SRC (PATTERN (insn)), var))
            {
              /* In unoptimized compilation, we shouldn't call delete_insn
                 except in jump.c doing warnings.  */
@@ -1231,7 +1513,7 @@ fixup_var_refs_insns (var, promoted_mode, unsignedp, insn, toplevel)
 
                 If we don't use an intermediate pseudo, such things as
                 address computations to make the address of VAR valid
-                if it is not can be placed beween the CALL_INSN and INSN.
+                if it is not can be placed between the CALL_INSN and INSN.
 
                 To make sure this doesn't happen, we record the destination
                 of the CALL_INSN and see if the next insn uses both that
@@ -1295,19 +1577,7 @@ fixup_var_refs_insns (var, promoted_mode, unsignedp, insn, toplevel)
                        replacements->old
                          = fixup_stack_1 (replacements->old, insn);
 
-                     /* We can not separate USE insns from the CALL_INSN
-                        that they belong to.  If this is a CALL_INSN, insert
-                        the move insn before the USE insns preceding it
-                        instead of immediately before the insn.  */
-                     if (GET_CODE (insn) == CALL_INSN)
-                       {
-                         insert_before = insn;
-                         while (GET_CODE (PREV_INSN (insert_before)) == INSN
-                                && GET_CODE (PATTERN (PREV_INSN (insert_before))) == USE)
-                           insert_before = PREV_INSN (insert_before);
-                       }
-                     else
-                       insert_before = insn;
+                     insert_before = insn;
 
                      /* If we are changing the mode, do a conversion.
                         This might be wasteful, but combine.c will
@@ -1475,10 +1745,9 @@ fixup_var_refs_1 (var, promoted_mode, loc, insn, replacements)
 
                  /* If the bytes and bits are counted differently, we
                     must adjust the offset.  */
-#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN
-                 offset = (GET_MODE_SIZE (is_mode)
-                           - GET_MODE_SIZE (wanted_mode) - offset);
-#endif
+                 if (BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN)
+                   offset = (GET_MODE_SIZE (is_mode)
+                             - GET_MODE_SIZE (wanted_mode) - offset);
 
                  pos %= GET_MODE_BITSIZE (wanted_mode);
 
@@ -1648,10 +1917,9 @@ fixup_var_refs_1 (var, promoted_mode, loc, insn, replacements)
                    rtx old_pos = XEXP (outerdest, 2);
                    rtx newmem;
 
-#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN
-                   offset = (GET_MODE_SIZE (is_mode)
-                             - GET_MODE_SIZE (wanted_mode) - offset);
-#endif
+                   if (BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN)
+                     offset = (GET_MODE_SIZE (is_mode)
+                               - GET_MODE_SIZE (wanted_mode) - offset);
 
                    pos %= GET_MODE_BITSIZE (wanted_mode);
 
@@ -1711,7 +1979,8 @@ fixup_var_refs_1 (var, promoted_mode, loc, insn, replacements)
            && (GET_CODE (SET_DEST (x)) == REG
                || (GET_CODE (SET_DEST (x)) == SUBREG
                    && GET_CODE (SUBREG_REG (SET_DEST (x))) == REG))
-           && x == single_set (PATTERN (insn)))
+           && GET_MODE (var) == promoted_mode
+           && x == single_set (insn))
          {
            rtx pat;
 
@@ -1756,7 +2025,8 @@ fixup_var_refs_1 (var, promoted_mode, loc, insn, replacements)
            && (GET_CODE (SET_SRC (x)) == REG
                || (GET_CODE (SET_SRC (x)) == SUBREG
                    && GET_CODE (SUBREG_REG (SET_SRC (x))) == REG))
-           && x == single_set (PATTERN (insn)))
+           && GET_MODE (var) == promoted_mode
+           && x == single_set (insn))
          {
            rtx pat;
 
@@ -1802,13 +2072,14 @@ fixup_var_refs_1 (var, promoted_mode, loc, insn, replacements)
              fixeddest = XEXP (fixeddest, 0);
            /* Convert (SUBREG (MEM)) to a MEM in a changed mode.  */
            if (GET_CODE (fixeddest) == SUBREG)
-             fixeddest = fixup_memory_subreg (fixeddest, insn, 0);
+             {
+               fixeddest = fixup_memory_subreg (fixeddest, insn, 0);
+               promoted_mode = GET_MODE (fixeddest);
+             }
            else
              fixeddest = fixup_stack_1 (fixeddest, insn);
 
-           temp = gen_reg_rtx (GET_MODE (SET_SRC (x)) == VOIDmode
-                               ? GET_MODE (fixeddest)
-                               : GET_MODE (SET_SRC (x)));
+           temp = gen_reg_rtx (promoted_mode);
 
            emit_insn_after (gen_move_insn (fixeddest,
                                            gen_lowpart (GET_MODE (fixeddest),
@@ -1860,10 +2131,9 @@ fixup_memory_subreg (x, insn, uncritical)
       && ! uncritical)
     abort ();
 
-#if BYTES_BIG_ENDIAN
-  offset += (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
-            - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)));
-#endif
+  if (BYTES_BIG_ENDIAN)
+    offset += (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+              - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)));
   addr = plus_constant (addr, offset);
   if (!flag_force_addr && memory_address_p (mode, addr))
     /* Shortcut if no insns need be emitted.  */
@@ -1920,28 +2190,6 @@ walk_fixup_memory_subreg (x, insn, uncritical)
   return x;
 }
 \f
-#if 0
-/* Fix up any references to stack slots that are invalid memory addresses
-   because they exceed the maximum range of a displacement.  */
-
-void
-fixup_stack_slots ()
-{
-  register rtx insn;
-
-  /* Did we generate a stack slot that is out of range
-     or otherwise has an invalid address?  */
-  if (invalid_stack_slot)
-    {
-      /* Yes.  Must scan all insns for stack-refs that exceed the limit.  */
-      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-       if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN
-           || GET_CODE (insn) == JUMP_INSN)
-         fixup_stack_1 (PATTERN (insn), insn);
-    }
-}
-#endif
-
 /* For each memory ref within X, if it refers to a stack slot
    with an out of range displacement, put the address in a temp register
    (emitting new insns before INSN to load these registers)
@@ -2062,26 +2310,31 @@ optimize_bit_field (body, insn, equiv_mem)
             and then for which byte of the word is wanted.  */
 
          register int offset = INTVAL (XEXP (bitfield, 2));
+         rtx insns;
+
          /* Adjust OFFSET to count bits from low-address byte.  */
-#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN
-         offset = (GET_MODE_BITSIZE (GET_MODE (XEXP (bitfield, 0)))
-                   - offset - INTVAL (XEXP (bitfield, 1)));
-#endif
+         if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
+           offset = (GET_MODE_BITSIZE (GET_MODE (XEXP (bitfield, 0)))
+                     - offset - INTVAL (XEXP (bitfield, 1)));
+
          /* Adjust OFFSET to count bytes from low-address byte.  */
          offset /= BITS_PER_UNIT;
          if (GET_CODE (XEXP (bitfield, 0)) == SUBREG)
            {
              offset += SUBREG_WORD (XEXP (bitfield, 0)) * UNITS_PER_WORD;
-#if BYTES_BIG_ENDIAN
-             offset -= (MIN (UNITS_PER_WORD,
-                             GET_MODE_SIZE (GET_MODE (XEXP (bitfield, 0))))
-                        - MIN (UNITS_PER_WORD,
-                               GET_MODE_SIZE (GET_MODE (memref))));
-#endif
+             if (BYTES_BIG_ENDIAN)
+               offset -= (MIN (UNITS_PER_WORD,
+                               GET_MODE_SIZE (GET_MODE (XEXP (bitfield, 0))))
+                          - MIN (UNITS_PER_WORD,
+                                 GET_MODE_SIZE (GET_MODE (memref))));
            }
 
-         memref = change_address (memref, mode, 
+         start_sequence ();
+         memref = change_address (memref, mode,
                                   plus_constant (XEXP (memref, 0), offset));
+         insns = get_insns ();
+         end_sequence ();
+         emit_insns_before (insns, insn);
 
          /* Store this memory reference where
             we found the bit field reference.  */
@@ -2114,7 +2367,9 @@ optimize_bit_field (body, insn, equiv_mem)
              rtx dest = SET_DEST (body);
 
              while (GET_CODE (dest) == SUBREG
-                    && SUBREG_WORD (dest) == 0)
+                    && SUBREG_WORD (dest) == 0
+                    && (GET_MODE_CLASS (GET_MODE (dest))
+                        == GET_MODE_CLASS (GET_MODE (SUBREG_REG (dest)))))
                dest = SUBREG_REG (dest);
 
              validate_change (insn, &SET_DEST (body), dest, 1);
@@ -2256,7 +2511,7 @@ instantiate_decls (fndecl, valid_only)
 {
   tree decl;
 
-  if (DECL_INLINE (fndecl))
+  if (DECL_INLINE (fndecl) || DECL_DEFER_OUTPUT (fndecl))
     /* When compiling an inline function, the obstack used for
        rtl allocation is the maybepermanent_obstack.  Calling
        `resume_temporary_allocation' switches us back to that
@@ -2275,7 +2530,7 @@ instantiate_decls (fndecl, valid_only)
   /* Now process all variables defined in the function or its subblocks. */
   instantiate_decls_1 (DECL_INITIAL (fndecl), valid_only);
 
-  if (DECL_INLINE (fndecl))
+  if (DECL_INLINE (fndecl) || DECL_DEFER_OUTPUT (fndecl))
     {
       /* Save all rtl allocated for this function by raising the
         high-water mark on the maybepermanent_obstack.  */
@@ -2471,7 +2726,7 @@ instantiate_virtual_regs_1 (loc, object, extra_insns)
       /* Handle special case of virtual register plus constant.  */
       if (CONSTANT_P (XEXP (x, 1)))
        {
-         rtx old;
+         rtx old, new_offset;
 
          /* Check for (plus (plus VIRT foo) (const_int)) first.  */
          if (GET_CODE (XEXP (x, 0)) == PLUS)
@@ -2518,18 +2773,26 @@ instantiate_virtual_regs_1 (loc, object, extra_insns)
              return 1;
            }
 
-         old = XEXP (x, 0);
-         XEXP (x, 0) = new;
-         new = plus_constant (XEXP (x, 1), offset);
+         new_offset = plus_constant (XEXP (x, 1), offset);
 
-         /* If the new constant is zero, try to replace the sum with its
-            first operand.  */
-         if (new == const0_rtx
-             && validate_change (object, loc, XEXP (x, 0), 0))
+         /* If the new constant is zero, try to replace the sum with just
+            the register.  */
+         if (new_offset == const0_rtx
+             && validate_change (object, loc, new, 0))
            return 1;
 
-         /* Next try to replace constant with new one.  */
-         if (!validate_change (object, &XEXP (x, 1), new, 0))
+         /* Next try to replace the register and new offset.
+            There are two changes to validate here and we can't assume that
+            in the case of old offset equals new just changing the register
+            will yield a valid insn.  In the interests of a little efficiency,
+            however, we only call validate change once (we don't queue up the
+            changes and then call apply_change_group). */
+
+         old = XEXP (x, 0);
+         if (offset == 0
+             ? ! validate_change (object, &XEXP (x, 0), new, 0)
+             : (XEXP (x, 0) = new,
+                ! validate_change (object, &XEXP (x, 1), new_offset, 0)))
            {
              if (! extra_insns)
                {
@@ -2540,15 +2803,16 @@ instantiate_virtual_regs_1 (loc, object, extra_insns)
              /* Otherwise copy the new constant into a register and replace
                 constant with that register.  */
              temp = gen_reg_rtx (Pmode);
+             XEXP (x, 0) = new;
              if (validate_change (object, &XEXP (x, 1), temp, 0))
-               emit_insn_before (gen_move_insn (temp, new), object);
+               emit_insn_before (gen_move_insn (temp, new_offset), object);
              else
                {
                  /* If that didn't work, replace this expression with a
                     register containing the sum.  */
 
-                 new = gen_rtx (PLUS, Pmode, XEXP (x, 0), new);
                  XEXP (x, 0) = old;
+                 new = gen_rtx (PLUS, Pmode, new, new_offset);
 
                  start_sequence ();
                  temp = force_operand (new, NULL_RTX);
@@ -2574,8 +2838,8 @@ instantiate_virtual_regs_1 (loc, object, extra_insns)
     case DIV:      case UDIV:
     case MOD:      case UMOD:
     case AND:      case IOR:      case XOR:
-    case LSHIFT:   case ASHIFT:   case ROTATE:
-    case ASHIFTRT: case LSHIFTRT: case ROTATERT:
+    case ROTATERT: case ROTATE:
+    case ASHIFTRT: case LSHIFTRT: case ASHIFT:
     case NE:       case EQ:
     case GE:       case GT:       case GEU:    case GTU:
     case LE:       case LT:       case LEU:    case LTU:
@@ -2742,7 +3006,25 @@ delete_handlers ()
         Also permit deletion of the nonlocal labels themselves
         if nothing local refers to them.  */
       if (GET_CODE (insn) == CODE_LABEL)
-       LABEL_PRESERVE_P (insn) = 0;
+       {
+         tree t, last_t;
+
+         LABEL_PRESERVE_P (insn) = 0;
+
+         /* Remove it from the nonlocal_label list, to avoid confusing
+            flow.  */
+         for (t = nonlocal_labels, last_t = 0; t;
+              last_t = t, t = TREE_CHAIN (t))
+           if (DECL_RTL (TREE_VALUE (t)) == insn)
+             break;
+         if (t)
+           {
+             if (! last_t)
+               nonlocal_labels = TREE_CHAIN (nonlocal_labels);
+             else
+               TREE_CHAIN (last_t) = TREE_CHAIN (t);
+           }
+       }
       if (GET_CODE (insn) == INSN
          && ((nonlocal_goto_handler_slot != 0
               && reg_mentioned_p (nonlocal_goto_handler_slot, PATTERN (insn)))
@@ -2860,11 +3142,7 @@ aggregate_value_p (exp)
 
   if (RETURN_IN_MEMORY (type))
     return 1;
-  if (flag_pcc_struct_return
-      && (TREE_CODE (type) == RECORD_TYPE
-         || TREE_CODE (type) == UNION_TYPE
-         || TREE_CODE (type) == QUAL_UNION_TYPE
-         || TREE_CODE (type) == ARRAY_TYPE))
+  if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type))
     return 1;
   /* Make sure we have suitable call-clobbered regs to return
      the value in; if not, we must return it in memory.  */
@@ -2897,7 +3175,8 @@ assign_parms (fndecl, second_time)
   register rtx entry_parm = 0;
   register rtx stack_parm = 0;
   CUMULATIVE_ARGS args_so_far;
-  enum machine_mode promoted_mode, passed_mode, nominal_mode;
+  enum machine_mode promoted_mode, passed_mode;
+  enum machine_mode nominal_mode, promoted_nominal_mode;
   int unsignedp;
   /* Total space needed so far for args on the stack,
      given as a constant and a tree-expression.  */
@@ -2919,8 +3198,9 @@ assign_parms (fndecl, second_time)
   /* Nonzero if the last arg is named `__builtin_va_alist',
      which is used on some machines for old-fashioned non-ANSI varargs.h;
      this should be stuck onto the stack as if it had arrived there.  */
-  int vararg
-    = (fnargs
+  int hide_last_arg
+    = (current_function_varargs
+       && fnargs
        && (parm = tree_last (fnargs)) != 0
        && DECL_NAME (parm)
        && (! strcmp (IDENTIFIER_POINTER (DECL_NAME (parm)),
@@ -2934,6 +3214,8 @@ assign_parms (fndecl, second_time)
        && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
           != void_type_node));
 
+  current_function_stdarg = stdarg;
+
   /* If the reg that the virtual arg pointer will be translated into is
      not a fixed reg or is the stack pointer, make a copy of the virtual
      arg pointer, and address parms via the copy.  The frame pointer is
@@ -2968,7 +3250,7 @@ assign_parms (fndecl, second_time)
     }
                               
   parm_reg_stack_loc = (rtx *) oballoc (nparmregs * sizeof (rtx));
-  bzero (parm_reg_stack_loc, nparmregs * sizeof (rtx));
+  bzero ((char *) parm_reg_stack_loc, nparmregs * sizeof (rtx));
 
 #ifdef INIT_CUMULATIVE_INCOMING_ARGS
   INIT_CUMULATIVE_INCOMING_ARGS (args_so_far, fntype, NULL_RTX);
@@ -2982,21 +3264,19 @@ assign_parms (fndecl, second_time)
 
   for (parm = fnargs; parm; parm = TREE_CHAIN (parm))
     {
-      int aggregate
-       = (TREE_CODE (TREE_TYPE (parm)) == ARRAY_TYPE
-          || TREE_CODE (TREE_TYPE (parm)) == RECORD_TYPE
-          || TREE_CODE (TREE_TYPE (parm)) == UNION_TYPE
-          || TREE_CODE (TREE_TYPE (parm)) == QUAL_UNION_TYPE);
+      int aggregate = AGGREGATE_TYPE_P (TREE_TYPE (parm));
       struct args_size stack_offset;
       struct args_size arg_size;
       int passed_pointer = 0;
+      int did_conversion = 0;
       tree passed_type = DECL_ARG_TYPE (parm);
+      tree nominal_type = TREE_TYPE (parm);
 
       /* Set LAST_NAMED if this is last named arg before some
         anonymous args.  We treat it as if it were anonymous too.  */
       int last_named = ((TREE_CHAIN (parm) == 0
                         || DECL_NAME (TREE_CHAIN (parm)) == 0)
-                       && (vararg || stdarg));
+                       && (stdarg || current_function_varargs));
 
       if (TREE_TYPE (parm) == error_mark_node
          /* This can happen after weird syntax errors
@@ -3012,13 +3292,13 @@ assign_parms (fndecl, second_time)
 
       /* For varargs.h function, save info about regs and stack space
         used by the individual args, not including the va_alist arg.  */
-      if (vararg && last_named)
+      if (hide_last_arg && last_named)
        current_function_args_info = args_so_far;
 
       /* Find mode of arg as it is passed, and mode of arg
         as it should be during execution of this function.  */
       passed_mode = TYPE_MODE (passed_type);
-      nominal_mode = TYPE_MODE (TREE_TYPE (parm));
+      nominal_mode = TYPE_MODE (nominal_type);
 
       /* If the parm's mode is VOID, its value doesn't matter,
         and avoid the usual things like emit_move_insn that could crash.  */
@@ -3028,32 +3308,37 @@ assign_parms (fndecl, second_time)
          continue;
        }
 
+      /* 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 (DECL_TRANSPARENT_UNION (parm)
+         || TYPE_TRANSPARENT_UNION (passed_type))
+       passed_type = TREE_TYPE (TYPE_FIELDS (passed_type));
+
+      /* See if this arg was passed by invisible reference.  It is if
+        it is an object whose size depends on the contents of the
+        object itself or if the machine requires these objects be passed
+        that way.  */
+
+      if ((TREE_CODE (TYPE_SIZE (passed_type)) != INTEGER_CST
+          && contains_placeholder_p (TYPE_SIZE (passed_type)))
+         || TREE_ADDRESSABLE (passed_type)
 #ifdef FUNCTION_ARG_PASS_BY_REFERENCE
-      /* See if this arg was passed by invisible reference.  */
-      if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, passed_mode,
-                                         passed_type, ! last_named))
+         || FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, passed_mode,
+                                             passed_type, ! last_named)
+#endif
+         )
        {
-         passed_type = build_pointer_type (passed_type);
+         passed_type = nominal_type = build_pointer_type (passed_type);
          passed_pointer = 1;
          passed_mode = nominal_mode = Pmode;
        }
-#endif
 
       promoted_mode = passed_mode;
 
 #ifdef PROMOTE_FUNCTION_ARGS
       /* Compute the mode in which the arg is actually extended to.  */
-      if (TREE_CODE (passed_type) == INTEGER_TYPE
-         || TREE_CODE (passed_type) == ENUMERAL_TYPE
-         || TREE_CODE (passed_type) == BOOLEAN_TYPE
-         || TREE_CODE (passed_type) == CHAR_TYPE
-         || TREE_CODE (passed_type) == REAL_TYPE
-         || TREE_CODE (passed_type) == POINTER_TYPE
-         || TREE_CODE (passed_type) == OFFSET_TYPE)
-       {
-         unsignedp = TREE_UNSIGNED (passed_type);
-         PROMOTE_MODE (promoted_mode, unsignedp, passed_type);
-       }
+      promoted_mode = promote_mode (passed_type, promoted_mode, &unsignedp, 1);
 #endif
 
       /* Let machine desc say which reg (if any) the parm arrives in.
@@ -3066,8 +3351,8 @@ assign_parms (fndecl, second_time)
                                 passed_type, ! last_named);
 #endif
 
-      if (entry_parm)
-       passed_mode = promoted_mode;
+      if (entry_parm == 0)
+       promoted_mode = passed_mode;
 
 #ifdef SETUP_INCOMING_VARARGS
       /* If this is the last named parameter, do any required setup for
@@ -3082,7 +3367,7 @@ assign_parms (fndecl, second_time)
         Also, indicate when RTL generation is to be suppressed.  */
       if (last_named && !varargs_setup)
        {
-         SETUP_INCOMING_VARARGS (args_so_far, passed_mode, passed_type,
+         SETUP_INCOMING_VARARGS (args_so_far, promoted_mode, passed_type,
                                  current_function_pretend_args_size,
                                  second_time);
          varargs_setup = 1;
@@ -3102,17 +3387,17 @@ assign_parms (fndecl, second_time)
         In this case, we call FUNCTION_ARG with NAMED set to 1 instead of
         0 as it was the previous time.  */
 
-      locate_and_pad_parm (passed_mode, passed_type,
+      locate_and_pad_parm (promoted_mode, passed_type,
 #ifdef STACK_PARMS_IN_REG_PARM_AREA
                           1,
 #else
 #ifdef FUNCTION_INCOMING_ARG
-                          FUNCTION_INCOMING_ARG (args_so_far, passed_mode,
+                          FUNCTION_INCOMING_ARG (args_so_far, promoted_mode,
                                                  passed_type,
                                                  (! last_named
                                                   || varargs_setup)) != 0,
 #else
-                          FUNCTION_ARG (args_so_far, passed_mode,
+                          FUNCTION_ARG (args_so_far, promoted_mode,
                                         passed_type,
                                         ! last_named || varargs_setup) != 0,
 #endif
@@ -3124,9 +3409,9 @@ assign_parms (fndecl, second_time)
          rtx offset_rtx = ARGS_SIZE_RTX (stack_offset);
 
          if (offset_rtx == const0_rtx)
-           stack_parm = gen_rtx (MEM, passed_mode, internal_arg_pointer);
+           stack_parm = gen_rtx (MEM, promoted_mode, internal_arg_pointer);
          else
-           stack_parm = gen_rtx (MEM, passed_mode,
+           stack_parm = gen_rtx (MEM, promoted_mode,
                                  gen_rtx (PLUS, Pmode,
                                           internal_arg_pointer, offset_rtx));
 
@@ -3137,7 +3422,7 @@ assign_parms (fndecl, second_time)
 
       /* If this parameter was passed both in registers and in the stack,
         use the copy on the stack.  */
-      if (MUST_PASS_IN_STACK (passed_mode, passed_type))
+      if (MUST_PASS_IN_STACK (promoted_mode, passed_type))
        entry_parm = 0;
 
 #ifdef FUNCTION_ARG_PARTIAL_NREGS
@@ -3151,7 +3436,7 @@ assign_parms (fndecl, second_time)
 
       if (entry_parm)
        {
-         int nregs = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, passed_mode,
+         int nregs = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, promoted_mode,
                                                  passed_type, ! last_named);
 
          if (nregs > 0)
@@ -3191,7 +3476,7 @@ assign_parms (fndecl, second_time)
             ??? When MAYBE_REG_PARM_STACK_SPACE is defined, we can't tell
             whether this parameter already has a stack slot allocated,
             because an arg block exists only if current_function_args_size
-            is larger than some threshhold, and we haven't calculated that
+            is larger than some threshold, and we haven't calculated that
             yet.  So, for now, we just assume that stack slots never exist
             in this case.  */
          || REG_PARM_STACK_SPACE (fndecl) > 0
@@ -3208,7 +3493,7 @@ assign_parms (fndecl, second_time)
 
       /* Update info on where next arg arrives in registers.  */
 
-      FUNCTION_ARG_ADVANCE (args_so_far, passed_mode,
+      FUNCTION_ARG_ADVANCE (args_so_far, promoted_mode,
                            passed_type, ! last_named);
 
       /* If this is our second time through, we are done with this parm. */
@@ -3220,7 +3505,7 @@ assign_parms (fndecl, second_time)
         We'll make another stack slot, if we need one.  */
       {
        int thisparm_boundary
-         = FUNCTION_ARG_BOUNDARY (passed_mode, passed_type);
+         = FUNCTION_ARG_BOUNDARY (promoted_mode, passed_type);
 
        if (GET_MODE_ALIGNMENT (nominal_mode) > thisparm_boundary)
          stack_parm = 0;
@@ -3244,11 +3529,10 @@ assign_parms (fndecl, second_time)
        {
          rtx offset_rtx;
 
-#if BYTES_BIG_ENDIAN
-         if (GET_MODE_SIZE (nominal_mode) < UNITS_PER_WORD)
+         if (BYTES_BIG_ENDIAN
+             && GET_MODE_SIZE (nominal_mode) < UNITS_PER_WORD)
            stack_offset.constant += (GET_MODE_SIZE (passed_mode)
                                      - GET_MODE_SIZE (nominal_mode));
-#endif
 
          offset_rtx = ARGS_SIZE_RTX (stack_offset);
          if (offset_rtx == const0_rtx)
@@ -3264,6 +3548,17 @@ assign_parms (fndecl, second_time)
        }
 #endif /* 0 */
 
+#ifdef STACK_REGS
+      /* We need this "use" info, because the gcc-register->stack-register
+        converter in reg-stack.c needs to know which registers are active
+        at the start of the function call.  The actual parameter loading
+        instructions are not always available then anymore, since they might
+        have been optimised away.  */
+
+      if (GET_CODE (entry_parm) == REG && !(hide_last_arg && last_named))
+         emit_insn (gen_rtx (USE, GET_MODE (entry_parm), entry_parm));
+#endif
+
       /* ENTRY_PARM is an RTX for the parameter as it arrives,
         in the mode in which it arrives.
         STACK_PARM is an RTX for a stack slot where the parameter can live
@@ -3283,8 +3578,9 @@ assign_parms (fndecl, second_time)
          /* If a BLKmode arrives in registers, copy it to a stack slot.  */
          if (GET_CODE (entry_parm) == REG)
            {
-             int size_stored = CEIL_ROUND (int_size_in_bytes (TREE_TYPE (parm)),
-                                           UNITS_PER_WORD);
+             int size_stored
+               = CEIL_ROUND (int_size_in_bytes (TREE_TYPE (parm)),
+                             UNITS_PER_WORD);
 
              /* Note that we will be storing an integral number of words.
                 So we have to be careful to ensure that we allocate an
@@ -3297,15 +3593,20 @@ assign_parms (fndecl, second_time)
              if (stack_parm == 0)
                {
                  stack_parm
-                   = assign_stack_local (GET_MODE (entry_parm), size_stored, 0);
-                 /* If this is a memory ref that contains aggregate components,
-                    mark it as such for cse and loop optimize.  */
+                   = assign_stack_local (GET_MODE (entry_parm),
+                                         size_stored, 0);
+
+                 /* If this is a memory ref that contains aggregate
+                    components, mark it as such for cse and loop optimize.  */
                  MEM_IN_STRUCT_P (stack_parm) = aggregate;
                }
 
              else if (PARM_BOUNDARY % BITS_PER_WORD != 0)
                abort ();
 
+             if (TREE_READONLY (parm))
+               RTX_UNCHANGING_P (stack_parm) = 1;
+
              move_block_from_reg (REGNO (entry_parm),
                                   validize_mem (stack_parm),
                                   size_stored / UNITS_PER_WORD,
@@ -3330,36 +3631,38 @@ assign_parms (fndecl, second_time)
             may need to do it in a wider mode.  */
 
          register rtx parmreg;
+         int regno, regnoi, regnor;
 
          unsignedp = TREE_UNSIGNED (TREE_TYPE (parm));
-         if (TREE_CODE (TREE_TYPE (parm)) == INTEGER_TYPE
-             || TREE_CODE (TREE_TYPE (parm)) == ENUMERAL_TYPE
-             || TREE_CODE (TREE_TYPE (parm)) == BOOLEAN_TYPE
-             || TREE_CODE (TREE_TYPE (parm)) == CHAR_TYPE
-             || TREE_CODE (TREE_TYPE (parm)) == REAL_TYPE
-             || TREE_CODE (TREE_TYPE (parm)) == POINTER_TYPE
-             || TREE_CODE (TREE_TYPE (parm)) == OFFSET_TYPE)
-           {
-             PROMOTE_MODE (nominal_mode, unsignedp, TREE_TYPE (parm));
-           }
 
-         parmreg = gen_reg_rtx (nominal_mode);
+         promoted_nominal_mode
+           = promote_mode (TREE_TYPE (parm), nominal_mode, &unsignedp, 0);
+
+         parmreg = gen_reg_rtx (promoted_nominal_mode);
          REG_USERVAR_P (parmreg) = 1;
 
          /* If this was an item that we received a pointer to, set DECL_RTL
             appropriately.  */
          if (passed_pointer)
            {
-             DECL_RTL (parm) = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (passed_type)), parmreg);
+             DECL_RTL (parm)
+               = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (passed_type)), parmreg);
              MEM_IN_STRUCT_P (DECL_RTL (parm)) = aggregate;
            }
          else
            DECL_RTL (parm) = parmreg;
 
          /* Copy the value into the register.  */
-         if (GET_MODE (parmreg) != GET_MODE (entry_parm))
+         if (nominal_mode != passed_mode
+             || promoted_nominal_mode != promoted_mode)
            {
-             /* If ENTRY_PARM is a hard register, it might be in a register
+             /* 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
+                a diffent mode than NOMINAL_MODE if it is being stored
+                promoted.
+
+                If ENTRY_PARM is a hard register, it might be in a register
                 not valid for operating in its mode (e.g., an odd-numbered
                 register for a DFmode).  In that case, moves are the only
                 thing valid, so we can't do a convert from there.  This
@@ -3377,8 +3680,12 @@ assign_parms (fndecl, second_time)
              emit_move_insn (tempreg, validize_mem (entry_parm));
 
              push_to_sequence (conversion_insns);
-             convert_move (parmreg, tempreg, unsignedp);
+             tempreg = convert_to_mode (nominal_mode, tempreg, unsignedp);
+
+             expand_assignment (parm,
+                                make_tree (nominal_type, tempreg), 0, 0);
              conversion_insns = get_insns ();
+             did_conversion = 1;
              end_sequence ();
            }
          else
@@ -3400,8 +3707,12 @@ assign_parms (fndecl, second_time)
              /* We can't use nominal_mode, because it will have been set to
                 Pmode above.  We must use the actual mode of the parm.  */
              parmreg = gen_reg_rtx (TYPE_MODE (TREE_TYPE (parm)));
+             REG_USERVAR_P (parmreg) = 1;
              emit_move_insn (parmreg, DECL_RTL (parm));
              DECL_RTL (parm) = parmreg;
+             /* STACK_PARM is the pointer, not the parm, and PARMREG is
+                now the parm.  */
+             stack_parm = 0;
            }
 #ifdef FUNCTION_ARG_CALLEE_COPIES
          /* If we are passed an arg by reference and it is our responsibility
@@ -3416,7 +3727,8 @@ assign_parms (fndecl, second_time)
                   && FUNCTION_ARG_CALLEE_COPIES (args_so_far,
                                                  TYPE_MODE (DECL_ARG_TYPE (parm)),
                                                  DECL_ARG_TYPE (parm),
-                                                 ! last_named))
+                                                 ! last_named)
+                  && ! TREE_ADDRESSABLE (DECL_ARG_TYPE (parm)))
            {
              rtx copy;
              tree type = DECL_ARG_TYPE (parm);
@@ -3428,42 +3740,71 @@ assign_parms (fndecl, second_time)
 
              if (TYPE_SIZE (type) == 0
                  || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
-               {
-                 /* This is a variable sized object.  */
-                 /* ??? Can we use expr_size here?  */
-                 rtx size_rtx = expand_expr (size_in_bytes (type), NULL_RTX,
-                                             TYPE_MODE (sizetype), 0);
-
-                 copy = gen_rtx (MEM, BLKmode,
-                                 allocate_dynamic_stack_space (size_rtx, NULL_RTX,
-                                                               TYPE_ALIGN (type)));
-               }
+               /* This is a variable sized object.  */
+               copy = gen_rtx (MEM, BLKmode,
+                               allocate_dynamic_stack_space
+                               (expr_size (parm), NULL_RTX,
+                                TYPE_ALIGN (type)));
              else
-               {
-                 int size = int_size_in_bytes (type);
-                 copy = assign_stack_temp (TYPE_MODE (type), size, 1);
-               }
+               copy = assign_stack_temp (TYPE_MODE (type),
+                                         int_size_in_bytes (type), 1);
+             MEM_IN_STRUCT_P (copy) = AGGREGATE_TYPE_P (type);
 
              store_expr (parm, copy, 0);
              emit_move_insn (parmreg, XEXP (copy, 0));
              conversion_insns = get_insns ();
+             did_conversion = 1;
              end_sequence ();
            }
 #endif /* FUNCTION_ARG_CALLEE_COPIES */
 
          /* In any case, record the parm's desired stack location
-            in case we later discover it must live in the stack.  */
-         if (REGNO (parmreg) >= nparmregs)
+            in case we later discover it must live in the stack. 
+
+            If it is a COMPLEX value, store the stack location for both
+            halves.  */
+
+         if (GET_CODE (parmreg) == CONCAT)
+           regno = MAX (REGNO (XEXP (parmreg, 0)), REGNO (XEXP (parmreg, 1)));
+         else
+           regno = REGNO (parmreg);
+
+         if (regno >= nparmregs)
            {
              rtx *new;
              int old_nparmregs = nparmregs;
-             nparmregs = REGNO (parmreg) + 5;
+
+             nparmregs = regno + 5;
              new = (rtx *) oballoc (nparmregs * sizeof (rtx));
-             bcopy (parm_reg_stack_loc, new, old_nparmregs * sizeof (rtx));
-             bzero (new + old_nparmregs, (nparmregs - old_nparmregs) * sizeof (rtx));
+             bcopy ((char *) parm_reg_stack_loc, (char *) new,
+                    old_nparmregs * sizeof (rtx));
+             bzero ((char *) (new + old_nparmregs),
+                    (nparmregs - old_nparmregs) * sizeof (rtx));
              parm_reg_stack_loc = new;
            }
-         parm_reg_stack_loc[REGNO (parmreg)] = stack_parm;
+
+         if (GET_CODE (parmreg) == CONCAT)
+           {
+             enum machine_mode submode = GET_MODE (XEXP (parmreg, 0));
+
+             regnor = REGNO (gen_realpart (submode, parmreg));
+             regnoi = REGNO (gen_imagpart (submode, parmreg));
+
+             if (stack_parm != 0)
+               {
+                 parm_reg_stack_loc[regnor]
+                   = gen_realpart (submode, stack_parm);
+                 parm_reg_stack_loc[regnoi]
+                   = gen_imagpart (submode, stack_parm);
+               }
+             else
+               {
+                 parm_reg_stack_loc[regnor] = 0;
+                 parm_reg_stack_loc[regnoi] = 0;
+               }
+           }
+         else
+           parm_reg_stack_loc[REGNO (parmreg)] = stack_parm;
 
          /* Mark the register as eliminable if we did no conversion
             and it was copied from memory at a fixed offset,
@@ -3472,14 +3813,38 @@ assign_parms (fndecl, second_time)
             an invalid address, such memory-equivalences
             as we make here would screw up life analysis for it.  */
          if (nominal_mode == passed_mode
+             && ! did_conversion
              && GET_CODE (entry_parm) == MEM
              && entry_parm == stack_parm
              && stack_offset.var == 0
              && reg_mentioned_p (virtual_incoming_args_rtx,
                                  XEXP (entry_parm, 0)))
-           REG_NOTES (get_last_insn ())
-             = gen_rtx (EXPR_LIST, REG_EQUIV,
-                        entry_parm, REG_NOTES (get_last_insn ()));
+           {
+             rtx linsn = get_last_insn ();
+
+             /* Mark complex types separately.  */
+             if (GET_CODE (parmreg) == CONCAT)
+               {
+                 REG_NOTES (linsn)
+                     = gen_rtx (EXPR_LIST, REG_EQUIV,
+                                parm_reg_stack_loc[regnoi], REG_NOTES (linsn));
+
+                 /* Now search backward for where we set the real part.  */
+                 for (; linsn != 0
+                      && ! reg_referenced_p (parm_reg_stack_loc[regnor],
+                                             PATTERN (linsn));
+                      linsn = prev_nonnote_insn (linsn))
+                   ;
+
+                 REG_NOTES (linsn)
+                     = gen_rtx (EXPR_LIST, REG_EQUIV,
+                                parm_reg_stack_loc[regnor], REG_NOTES (linsn));
+               }
+             else
+               REG_NOTES (linsn)
+                = gen_rtx (EXPR_LIST, REG_EQUIV,
+                           entry_parm, REG_NOTES (linsn));
+           }
 
          /* For pointer data type, suggest pointer register.  */
          if (TREE_CODE (TREE_TYPE (parm)) == POINTER_TYPE)
@@ -3490,7 +3855,7 @@ assign_parms (fndecl, second_time)
          /* Value must be stored in the stack slot STACK_PARM
             during function execution.  */
 
-         if (passed_mode != nominal_mode)
+         if (promoted_mode != nominal_mode)
            {
              /* Conversion is required.   */
              rtx tempreg = gen_reg_rtx (GET_MODE (entry_parm));
@@ -3501,6 +3866,7 @@ assign_parms (fndecl, second_time)
              entry_parm = convert_to_mode (nominal_mode, tempreg,
                                            TREE_UNSIGNED (TREE_TYPE (parm)));
              conversion_insns = get_insns ();
+             did_conversion = 1;
              end_sequence ();
            }
 
@@ -3516,7 +3882,7 @@ assign_parms (fndecl, second_time)
                  MEM_IN_STRUCT_P (stack_parm) = aggregate;
                }
 
-             if (passed_mode != nominal_mode)
+             if (promoted_mode != nominal_mode)
                {
                  push_to_sequence (conversion_insns);
                  emit_move_insn (validize_mem (stack_parm),
@@ -3535,8 +3901,15 @@ assign_parms (fndecl, second_time)
       /* If this "parameter" was the place where we are receiving the
         function's incoming structure pointer, set up the result.  */
       if (parm == function_result_decl)
-       DECL_RTL (DECL_RESULT (fndecl))
-         = gen_rtx (MEM, DECL_MODE (DECL_RESULT (fndecl)), DECL_RTL (parm));
+       {
+         tree result = DECL_RESULT (fndecl);
+         tree restype = TREE_TYPE (result);
+
+         DECL_RTL (result)
+           = gen_rtx (MEM, DECL_MODE (result), DECL_RTL (parm));
+
+         MEM_IN_STRUCT_P (DECL_RTL (result)) = AGGREGATE_TYPE_P (restype);
+       }
 
       if (TREE_THIS_VOLATILE (parm))
        MEM_VOLATILE_P (DECL_RTL (parm)) = 1;
@@ -3584,13 +3957,13 @@ assign_parms (fndecl, second_time)
   /* See how many bytes, if any, of its args a function should try to pop
      on return.  */
 
-  current_function_pops_args = RETURN_POPS_ARGS (TREE_TYPE (fndecl),
+  current_function_pops_args = RETURN_POPS_ARGS (fndecl, TREE_TYPE (fndecl),
                                                 current_function_args_size);
 
-  /* For stdarg.h function, save info about regs and stack space
-     used by the named args.  */
+  /* For stdarg.h function, save info about
+     regs and stack space used by the named args.  */
 
-  if (stdarg)
+  if (!hide_last_arg)
     current_function_args_info = args_so_far;
 
   /* Set the rtx used for the function return value.  Put this in its
@@ -3621,18 +3994,12 @@ promoted_input_arg (regno, pmode, punsignedp)
        arg = TREE_CHAIN (arg))
     if (GET_CODE (DECL_INCOMING_RTL (arg)) == REG
        && REGNO (DECL_INCOMING_RTL (arg)) == regno
-       && (TREE_CODE (TREE_TYPE (arg)) == INTEGER_TYPE
-           || TREE_CODE (TREE_TYPE (arg)) == ENUMERAL_TYPE
-           || TREE_CODE (TREE_TYPE (arg)) == BOOLEAN_TYPE
-           || TREE_CODE (TREE_TYPE (arg)) == CHAR_TYPE
-           || TREE_CODE (TREE_TYPE (arg)) == REAL_TYPE
-           || TREE_CODE (TREE_TYPE (arg)) == POINTER_TYPE
-           || TREE_CODE (TREE_TYPE (arg)) == OFFSET_TYPE))
+       && TYPE_MODE (DECL_ARG_TYPE (arg)) == TYPE_MODE (TREE_TYPE (arg)))
       {
        enum machine_mode mode = TYPE_MODE (TREE_TYPE (arg));
        int unsignedp = TREE_UNSIGNED (TREE_TYPE (arg));
 
-       PROMOTE_MODE (mode, unsignedp, TREE_TYPE (arg));
+       mode = promote_mode (TREE_TYPE (arg), mode, &unsignedp, 1);
        if (mode == GET_MODE (DECL_INCOMING_RTL (arg))
            && mode != DECL_MODE (arg))
          {
@@ -3676,8 +4043,6 @@ promoted_input_arg (regno, pmode, punsignedp)
     callers pass in the total size of args so far as
     initial_offset_ptr. arg_size_ptr is always positive.*/
 
-static void pad_to_arg_alignment (), pad_below ();
-
 void
 locate_and_pad_parm (passed_mode, type, in_regs, fndecl,
                     initial_offset_ptr, offset_ptr, arg_size_ptr)
@@ -3736,7 +4101,7 @@ locate_and_pad_parm (passed_mode, type, in_regs, fndecl,
       offset_ptr->constant = - initial_offset_ptr->constant;
       offset_ptr->var = 0;
     }
-  if (where_pad == upward
+  if (where_pad != none
       && (TREE_CODE (sizetree) != INTEGER_CST
          || ((TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY)))
     sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT);
@@ -3756,9 +4121,6 @@ locate_and_pad_parm (passed_mode, type, in_regs, fndecl,
       arg_size_ptr->constant = (- initial_offset_ptr->constant -
                                offset_ptr->constant); 
     }
-/*  ADD_PARM_SIZE (*arg_size_ptr, sizetree); */
-  if (where_pad == downward)
-    pad_below (arg_size_ptr, passed_mode, sizetree);
 #else /* !ARGS_GROW_DOWNWARD */
   pad_to_arg_alignment (initial_offset_ptr, boundary);
   *offset_ptr = *initial_offset_ptr;
@@ -3768,15 +4130,19 @@ locate_and_pad_parm (passed_mode, type, in_regs, fndecl,
     sizetree = size_int (PUSH_ROUNDING (TREE_INT_CST_LOW (sizetree)));
 #endif
 
+  /* Pad_below needs the pre-rounded size to know how much to pad below
+     so this must be done before rounding up.  */
+  if (where_pad == downward
+    /* However, BLKmode args passed in regs have their padding done elsewhere.
+       The stack slot must be able to hold the entire register.  */
+      && !(in_regs && passed_mode == BLKmode))
+    pad_below (offset_ptr, passed_mode, sizetree);
+
   if (where_pad != none
       && (TREE_CODE (sizetree) != INTEGER_CST
          || ((TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY)))
     sizetree = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT);
 
-  /* This must be done after rounding sizetree, so that it will subtract
-     the same value that we explicitly add below.  */
-  if (where_pad == downward)
-    pad_below (offset_ptr, passed_mode, sizetree);
   ADD_PARM_SIZE (*arg_size_ptr, sizetree);
 #endif /* ARGS_GROW_DOWNWARD */
 }
@@ -3870,21 +4236,18 @@ uninitialized_vars_warning (block)
             because assigning the fields one by one can fail to convince
             flow.c that the entire aggregate was initialized.
             Unions are troublesome because members may be shorter.  */
-         && TREE_CODE (TREE_TYPE (decl)) != RECORD_TYPE
-         && TREE_CODE (TREE_TYPE (decl)) != UNION_TYPE
-         && TREE_CODE (TREE_TYPE (decl)) != QUAL_UNION_TYPE
-         && TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE
+         && ! AGGREGATE_TYPE_P (TREE_TYPE (decl))
          && DECL_RTL (decl) != 0
          && GET_CODE (DECL_RTL (decl)) == REG
          && regno_uninitialized (REGNO (DECL_RTL (decl))))
        warning_with_decl (decl,
-                          "`%s' may be used uninitialized in this function");
+                          "`%s' might be used uninitialized in this function");
       if (TREE_CODE (decl) == VAR_DECL
          && DECL_RTL (decl) != 0
          && GET_CODE (DECL_RTL (decl)) == REG
          && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl))))
        warning_with_decl (decl,
-                          "variable `%s' may be clobbered by `longjmp' or `vfork'");
+                          "variable `%s' might be clobbered by `longjmp' or `vfork'");
     }
   for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub))
     uninitialized_vars_warning (sub);
@@ -3894,8 +4257,7 @@ uninitialized_vars_warning (block)
    but for arguments instead of local variables.  */
 
 void
-setjmp_args_warning (block)
-     tree block;
+setjmp_args_warning ()
 {
   register tree decl;
   for (decl = DECL_ARGUMENTS (current_function_decl);
@@ -3903,7 +4265,7 @@ setjmp_args_warning (block)
     if (DECL_RTL (decl) != 0
        && GET_CODE (DECL_RTL (decl)) == REG
        && regno_clobbered_at_setjmp (REGNO (DECL_RTL (decl))))
-      warning_with_decl (decl, "argument `%s' may be clobbered by `longjmp' or `vfork'");
+      warning_with_decl (decl, "argument `%s' might be clobbered by `longjmp' or `vfork'");
 }
 
 /* If this function call setjmp, put all vars into the stack
@@ -4091,7 +4453,9 @@ trampoline_address (function)
   /* Find an existing trampoline and return it.  */
   for (link = trampoline_list; link; link = TREE_CHAIN (link))
     if (TREE_PURPOSE (link) == function)
-      return XEXP (RTL_EXPR_RTL (TREE_VALUE (link)), 0);
+      return
+       round_trampoline_addr (XEXP (RTL_EXPR_RTL (TREE_VALUE (link)), 0));
+
   for (fp = outer_function_chain; fp; fp = fp->next)
     for (link = fp->trampoline_list; link; link = TREE_CHAIN (link))
       if (TREE_PURPOSE (link) == function)
@@ -4133,7 +4497,8 @@ trampoline_address (function)
      by expand_function_end.  */
   if (fp != 0)
     {
-      push_obstacks (fp->current_obstack, fp->function_maybepermanent_obstack);
+      push_obstacks (fp->function_maybepermanent_obstack,
+                    fp->function_maybepermanent_obstack);
       rtlexp = make_node (RTL_EXPR);
       RTL_EXPR_RTL (rtlexp) = tramp;
       fp->trampoline_list = tree_cons (function, rtlexp, fp->trampoline_list);
@@ -4180,36 +4545,34 @@ round_trampoline_addr (tramp)
    duplicate portions of the RTL code.  Call identify_blocks before
    changing the RTL, and call reorder_blocks after.  */
 
-static int all_blocks ();
-static tree blocks_nreverse ();
-
-/* Put all this function's BLOCK nodes into a vector, and return it.
+/* Put all this function's BLOCK nodes including those that are chained
+   onto the first block into a vector, and return it.
    Also store in each NOTE for the beginning or end of a block
    the index of that block in the vector.
-   The arguments are TOP_BLOCK, the top-level block of the function,
+   The arguments are BLOCK, the chain of top-level blocks of the function,
    and INSNS, the insn chain of the function.  */
 
 tree *
-identify_blocks (top_block, insns)
-     tree top_block;
+identify_blocks (block, insns)
+     tree block;
      rtx insns;
 {
   int n_blocks;
   tree *block_vector;
   int *block_stack;
   int depth = 0;
-  int next_block_number = 0;
-  int current_block_number = 0;
+  int next_block_number = 1;
+  int current_block_number = 1;
   rtx insn;
 
-  if (top_block == 0)
+  if (block == 0)
     return 0;
 
-  n_blocks = all_blocks (top_block, 0);
+  n_blocks = all_blocks (block, 0);
   block_vector = (tree *) xmalloc (n_blocks * sizeof (tree));
   block_stack = (int *) alloca (n_blocks * sizeof (int));
 
-  all_blocks (top_block, block_vector);
+  all_blocks (block, block_vector);
 
   for (insn = insns; insn; insn = NEXT_INSN (insn))
     if (GET_CODE (insn) == NOTE)
@@ -4227,6 +4590,9 @@ identify_blocks (top_block, insns)
          }
       }
 
+  if (n_blocks != next_block_number)
+    abort ();
+
   return block_vector;
 }
 
@@ -4237,19 +4603,20 @@ identify_blocks (top_block, insns)
    Returns the current top-level block.  */
 
 tree
-reorder_blocks (block_vector, top_block, insns)
+reorder_blocks (block_vector, block, insns)
      tree *block_vector;
-     tree top_block;
+     tree block;
      rtx insns;
 {
-  tree current_block = top_block;
+  tree current_block = block;
   rtx insn;
 
   if (block_vector == 0)
-    return top_block;
+    return block;
 
-  /* Prune the old tree away, so that it doesn't get in the way.  */
+  /* Prune the old trees away, so that it doesn't get in the way.  */
   BLOCK_SUBBLOCKS (current_block) = 0;
+  BLOCK_CHAIN (current_block) = 0;
 
   for (insn = insns; insn; insn = NEXT_INSN (insn))
     if (GET_CODE (insn) == NOTE)
@@ -4277,6 +4644,8 @@ reorder_blocks (block_vector, top_block, insns)
          }
       }
 
+  BLOCK_SUBBLOCKS (current_block)
+    = blocks_nreverse (BLOCK_SUBBLOCKS (current_block));
   return current_block;
 }
 
@@ -4297,31 +4666,38 @@ blocks_nreverse (t)
   return prev;
 }
 
-/* Count the subblocks of BLOCK, and list them all into the vector VECTOR.
-   Also clear TREE_ASM_WRITTEN in all blocks.  */
+/* Count the subblocks of the list starting with BLOCK, and list them
+   all into the vector VECTOR.  Also clear TREE_ASM_WRITTEN in all
+   blocks.  */
 
 static int
 all_blocks (block, vector)
      tree block;
      tree *vector;
 {
-  int n_blocks = 1;
-  tree subblocks; 
+  int n_blocks = 0;
+
+  while (block)
+    {
+      TREE_ASM_WRITTEN (block) = 0;
 
-  TREE_ASM_WRITTEN (block) = 0;
-  /* Record this block.  */
-  if (vector)
-    vector[0] = block;
+      /* Record this block.  */
+      if (vector)
+       vector[n_blocks] = block;
 
-  /* Record the subblocks, and their subblocks.  */
-  for (subblocks = BLOCK_SUBBLOCKS (block);
-       subblocks; subblocks = BLOCK_CHAIN (subblocks))
-    n_blocks += all_blocks (subblocks, vector ? vector + n_blocks : 0);
+      ++n_blocks;
+      
+      /* Record the subblocks, and their subblocks...  */
+      n_blocks += all_blocks (BLOCK_SUBBLOCKS (block),
+                             vector ? vector + n_blocks : 0);
+      block = BLOCK_CHAIN (block);
+    }
 
   return n_blocks;
 }
 \f
 /* Build bytecode call descriptor for function SUBR. */
+
 rtx
 bc_build_calldesc (subr)
   tree subr;
@@ -4463,9 +4839,8 @@ init_function_start (subr, filename, line)
   /* No RTL_EXPRs in this function yet.  */
   rtl_expr_chain = 0;
 
-  /* We have not allocated any temporaries yet.  */
-  temp_slots = 0;
-  temp_slot_level = 0;
+  /* Set up to allocate temporaries.  */
+  init_temp_slots ();
 
   /* Within function body, compute a type's size as soon it is laid out.  */
   immediate_size_expand++;
@@ -4478,9 +4853,6 @@ init_function_start (subr, filename, line)
 
   current_function_outgoing_args_size = 0;
 
-  /* Initialize the insn lengths.  */
-  init_insn_lengths ();
-
   /* Prevent ever trying to delete the first instruction of a function.
      Also tell final how to output a linenum before the function prologue.  */
   emit_line_note (filename, line);
@@ -4502,14 +4874,11 @@ init_function_start (subr, filename, line)
   /* Warn if this value is an aggregate type,
      regardless of which calling convention we are using for it.  */
   if (warn_aggregate_return
-      && (TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == RECORD_TYPE
-         || TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == UNION_TYPE
-         || TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == QUAL_UNION_TYPE
-         || TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == ARRAY_TYPE))
+      && AGGREGATE_TYPE_P (TREE_TYPE (DECL_RESULT (subr))))
     warning ("function returns an aggregate");
 
   current_function_returns_pointer
-    = (TREE_CODE (TREE_TYPE (DECL_RESULT (subr))) == POINTER_TYPE);
+    = POINTER_TYPE_P (TREE_TYPE (DECL_RESULT (subr)));
 
   /* Indicate that we need to distinguish between the return value of the
      present function and the return value of a function being called.  */
@@ -4521,8 +4890,9 @@ init_function_start (subr, filename, line)
   /* Indicate we have no need of a frame pointer yet.  */
   frame_pointer_needed = 0;
 
-  /* By default assume not varargs.  */
+  /* By default assume not varargs or stdarg.  */
   current_function_varargs = 0;
+  current_function_stdarg = 0;
 }
 
 /* Indicate that the current function uses extra args
@@ -4536,6 +4906,11 @@ mark_varargs ()
 
 /* Expand a call to __main at the beginning of a possible main function.  */
 
+#if defined(INIT_SECTION_ASM_OP) && !defined(INVOKE__main)
+#undef HAS_INIT_SECTION
+#define HAS_INIT_SECTION
+#endif
+
 void
 expand_main_function ()
 {
@@ -4543,10 +4918,10 @@ expand_main_function ()
     {
       /* The zero below avoids a possible parse error */
       0;
-#if !defined (INIT_SECTION_ASM_OP) || defined (INVOKE__main)
+#if !defined (HAS_INIT_SECTION)
       emit_library_call (gen_rtx (SYMBOL_REF, Pmode, NAME__MAIN), 0,
                         VOIDmode, 0);
-#endif /* not INIT_SECTION_ASM_OP or INVOKE__main */
+#endif /* not HAS_INIT_SECTION */
     }
 }
 \f
@@ -4615,7 +4990,7 @@ bc_expand_function_end ()
      to BC_END_FUNCTION (), since that will cause the bytecode
      segment to be finished off and closed. */
 
-  fixup_gotos (0, 0, 0, 0, 0);
+  expand_fixups (NULL_RTX);
 
   ptrconsts = bc_end_function ();
 
@@ -4662,7 +5037,13 @@ expand_function_start (subr, parms_have_cleanups)
   if (current_function_needs_context)
     {
       last_ptr = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0);
-      emit_move_insn (last_ptr, static_chain_incoming_rtx);
+
+#ifdef SMALL_REGISTER_CLASSES
+      /* Delay copying static chain if it is not a register to avoid
+        conflicts with regs used for parameters.  */
+      if (GET_CODE (static_chain_incoming_rtx) == REG)
+#endif
+        emit_move_insn (last_ptr, static_chain_incoming_rtx);
     }
 
   /* If the parameters of this function need cleaning up, get a label
@@ -4695,7 +5076,7 @@ expand_function_start (subr, parms_have_cleanups)
   if (aggregate_value_p (DECL_RESULT (subr)))
     {
       /* Returning something that won't go in a register.  */
-      register rtx value_address;
+      register rtx value_address = 0;
 
 #ifdef PCC_STATIC_STRUCT_RETURN
       if (current_function_returns_pcc_struct)
@@ -4716,9 +5097,12 @@ expand_function_start (subr, parms_have_cleanups)
            }
        }
       if (value_address)
-       DECL_RTL (DECL_RESULT (subr))
-         = gen_rtx (MEM, DECL_MODE (DECL_RESULT (subr)),
-                    value_address);
+       {
+         DECL_RTL (DECL_RESULT (subr))
+           = gen_rtx (MEM, DECL_MODE (DECL_RESULT (subr)), value_address);
+         MEM_IN_STRUCT_P (DECL_RTL (DECL_RESULT (subr)))
+           = AGGREGATE_TYPE_P (TREE_TYPE (DECL_RESULT (subr)));
+       }
     }
   else if (DECL_MODE (DECL_RESULT (subr)) == VOIDmode)
     /* If return mode is void, this decl rtl should not be used.  */
@@ -4731,17 +5115,12 @@ expand_function_start (subr, parms_have_cleanups)
         after the cleanups are done.  */
 
       enum machine_mode mode = DECL_MODE (DECL_RESULT (subr));
+
 #ifdef PROMOTE_FUNCTION_RETURN
       tree type = TREE_TYPE (DECL_RESULT (subr));
       int unsignedp = TREE_UNSIGNED (type);
 
-      if (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == ENUMERAL_TYPE
-         || TREE_CODE (type) == BOOLEAN_TYPE || TREE_CODE (type) == CHAR_TYPE
-         || TREE_CODE (type) == REAL_TYPE || TREE_CODE (type) == POINTER_TYPE
-         || TREE_CODE (type) == OFFSET_TYPE)
-       {
-         PROMOTE_MODE (mode, unsignedp, type);
-       }
+      mode = promote_mode (type, mode, &unsignedp, 1);
 #endif
 
       DECL_RTL (DECL_RESULT (subr)) = gen_reg_rtx (mode);
@@ -4772,6 +5151,15 @@ expand_function_start (subr, parms_have_cleanups)
 
   assign_parms (subr, 0);
 
+#ifdef SMALL_REGISTER_CLASSES
+  /* Copy the static chain now if it wasn't a register.  The delay is to
+     avoid conflicts with the parameter passing registers.  */
+
+  if (current_function_needs_context)
+      if (GET_CODE (static_chain_incoming_rtx) != REG)
+        emit_move_insn (last_ptr, static_chain_incoming_rtx);
+#endif
+
   /* The following was moved from init_function_start.
      The move is supposed to make sdb output more accurate.  */
   /* Indicate the beginning of the function body,
@@ -4795,11 +5183,23 @@ expand_function_start (subr, parms_have_cleanups)
 
   /* Fetch static chain values for containing functions.  */
   tem = decl_function_context (current_function_decl);
-  /* If not doing stupid register allocation, then start off with the static
-     chain pointer in a pseudo register.  Otherwise, we use the stack
-     address that was generated above.  */
+  /* If not doing stupid register allocation copy the static chain
+     pointer into a pseudo.  If we have small register classes, copy the
+     value from memory if static_chain_incoming_rtx is a REG.  If we do
+     stupid register allocation, we use the stack address generated above.  */
   if (tem && ! obey_regdecls)
-    last_ptr = copy_to_reg (static_chain_incoming_rtx);
+    {
+#ifdef SMALL_REGISTER_CLASSES
+      /* If the static chain originally came in a register, put it back
+        there, then move it out in the next insn.  The reason for
+        this peculiar code is to satisfy function integration.  */
+      if (GET_CODE (static_chain_incoming_rtx) == REG)
+       emit_move_insn (static_chain_incoming_rtx, last_ptr);
+#endif
+
+      last_ptr = copy_to_reg (static_chain_incoming_rtx);
+    }
+
   context_display = 0;
   while (tem)
     {
@@ -4817,6 +5217,12 @@ expand_function_start (subr, parms_have_cleanups)
 #endif
       last_ptr = copy_to_reg (gen_rtx (MEM, Pmode,
                                       memory_address (Pmode, last_ptr)));
+
+      /* If we are not optimizing, ensure that we know that this
+        piece of context is live over the entire function.  */
+      if (! optimize)
+       save_expr_regs = gen_rtx (EXPR_LIST, VOIDmode, last_ptr,
+                                 save_expr_regs);
     }
 
   /* After the display initializations is where the tail-recursion label
@@ -4833,14 +5239,16 @@ expand_function_start (subr, parms_have_cleanups)
 }
 \f
 /* Generate RTL for the end of the current function.
-   FILENAME and LINE are the current position in the source file.  */
+   FILENAME and LINE are the current position in the source file. 
 
-/* It is up to language-specific callers to do cleanups for parameters.  */
+   It is up to language-specific callers to do cleanups for parameters--
+   or else, supply 1 for END_BINDINGS and we will call expand_end_bindings.  */
 
 void
-expand_function_end (filename, line)
+expand_function_end (filename, line, end_bindings)
      char *filename;
      int line;
+     int end_bindings;
 {
   register int i;
   tree link;
@@ -4858,7 +5266,9 @@ expand_function_end (filename, line)
      on a machine that fails to restore the registers.  */
   if (NON_SAVING_SETJMP && current_function_calls_setjmp)
     {
-      setjmp_protect (DECL_INITIAL (current_function_decl));
+      if (DECL_INITIAL (current_function_decl) != error_mark_node)
+       setjmp_protect (DECL_INITIAL (current_function_decl));
+
       setjmp_protect_args ();
     }
 #endif
@@ -4903,18 +5313,17 @@ expand_function_end (filename, line)
       emit_insns_before (seq, tail_recursion_reentry);
     }
 
-#if 0  /* I think unused parms are legitimate enough.  */
-  /* Warn about unused parms.  */
-  if (warn_unused)
+  /* Warn about unused parms if extra warnings were specified.  */
+  if (warn_unused && extra_warnings)
     {
-      rtx decl;
+      tree decl;
 
       for (decl = DECL_ARGUMENTS (current_function_decl);
           decl; decl = TREE_CHAIN (decl))
-       if (! TREE_USED (decl) && TREE_CODE (decl) == VAR_DECL)
+       if (! TREE_USED (decl) && TREE_CODE (decl) == PARM_DECL
+           && DECL_NAME (decl) && ! DECL_ARTIFICIAL (decl))
          warning_with_decl (decl, "unused parameter `%s'");
     }
-#endif
 
   /* Delete handlers for nonlocal gotos if nothing uses them.  */
   if (nonlocal_goto_handler_slot != 0 && !current_function_has_nonlocal_label)
@@ -4970,6 +5379,10 @@ expand_function_end (filename, line)
   if (return_label)
     emit_label (return_label);
 
+  /* C++ uses this.  */
+  if (end_bindings)
+    expand_end_bindings (0, 0, 0);
+
   /* If we had calls to alloca, and this machine needs
      an accurate stack pointer to exit the function,
      insert some code to save and restore the stack pointer.  */
@@ -5056,7 +5469,7 @@ expand_function_end (filename, line)
   /* If you have any cleanups to do at this point,
      and they need to create temporary variables,
      then you will lose.  */
-  fixup_gotos (NULL_PTR, NULL_RTX, NULL_TREE, get_insns (), 0);
+  expand_fixups (get_insns ());
 }
 \f
 /* These arrays record the INSN_UIDs of the prologue and epilogue insns.  */
@@ -5118,7 +5531,7 @@ contains (insn, vec)
   return 0;
 }
 
-/* Generate the prologe and epilogue RTL if the machine supports it.  Thread
+/* Generate the prologue and epilogue RTL if the machine supports it.  Thread
    this into place with notes indicating where the prologue ends and where
    the epilogue begins.  Update the basic block information when possible.  */
 
@@ -5177,11 +5590,13 @@ thread_prologue_and_epilogue_insns (f)
 
              NEXT_INSN (PREV_INSN (tem)) = NEXT_INSN (tem);
              PREV_INSN (NEXT_INSN (tem)) = PREV_INSN (tem);
-             if (! first_use)
-               first_use = tem;
-             if (last_use)
-               NEXT_INSN (last_use) = tem;
-             else
+             if (first_use)
+               {
+                 NEXT_INSN (tem) = first_use;
+                 PREV_INSN (first_use) = tem;
+               }
+             first_use = tem;
+             if (!last_use)
                last_use = tem;
            }