OSDN Git Service

(fold_rtx, case PLUS): When seeing if negative of constant is around,
[pf3gnuchains/gcc-fork.git] / gcc / function.c
index 1edd6a1..7560866 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, 93, 1994 Free Software Foundation, Inc.
+   Copyright (C) 1987, 88, 89, 91-96, 1997 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
@@ -38,12 +39,11 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
    then scans all the RTL instructions so far generated to correct them.  */
 
 #include "config.h"
-
 #include <stdio.h>
-
 #include "rtl.h"
 #include "tree.h"
 #include "flags.h"
+#include "except.h"
 #include "function.h"
 #include "insn-flags.h"
 #include "expr.h"
@@ -56,11 +56,16 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "basic-block.h"
 #include "obstack.h"
 #include "bytecode.h"
+#include "bc-emit.h"
+
+#ifndef TRAMPOLINE_ALIGNMENT
+#define TRAMPOLINE_ALIGNMENT FUNCTION_BOUNDARY
+#endif
 
 /* 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 +157,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 +172,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.  */
 
@@ -258,7 +267,7 @@ rtx arg_pointer_save_area;
 /* Offset to end of allocated area of stack frame.
    If stack grows down, this is the address of the last stack slot allocated.
    If stack grows up, this is the address for the next slot.  */
-int frame_offset;
+HOST_WIDE_INT frame_offset;
 
 /* List (chain of TREE_LISTs) of static chains for containing functions.
    Each link has a FUNCTION_DECL in the TREE_PURPOSE and a reg rtx
@@ -295,13 +304,6 @@ static int max_parm_reg;
    if we discover that that parm must go in the stack.  */
 static rtx *parm_reg_stack_loc;
 
-#if 0  /* Turned off because 0 seems to work just as well.  */
-/* Cleanup lists are required for binding levels regardless of whether
-   that binding level has cleanups or not.  This node serves as the
-   cleanup list whenever an empty list is required.  */
-static tree empty_cleanup_list;
-#endif
-
 /* Nonzero once virtual register instantiation has been done.
    assign_stack_local uses frame_pointer_rtx when this is nonzero.  */
 static int virtuals_instantiated;
@@ -309,8 +311,8 @@ 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) ();
+void (*save_machine_status) PROTO((struct function *));
+void (*restore_machine_status) PROTO((struct function *));
 
 /* 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
@@ -318,10 +320,6 @@ void (*restore_machine_status) ();
 
 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 ();
 \f
 /* In order to evaluate some expressions, such as function calls returning
    structures in memory, we need to temporarily allocate stack locations.
@@ -345,7 +343,7 @@ struct temp_slot
 {
   /* Points to next temporary slot.  */
   struct temp_slot *next;
-  /* The rtx to used to reference the slot. */
+  /* 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.  */
@@ -356,10 +354,18 @@ struct temp_slot
   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.  */
@@ -411,7 +417,8 @@ struct fixup_replacement
 
 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));
+                                      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));
@@ -461,7 +468,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));
 
@@ -473,16 +481,19 @@ push_function_context ()
   p->pops_args = current_function_pops_args;
   p->returns_struct = current_function_returns_struct;
   p->returns_pcc_struct = current_function_returns_pcc_struct;
+  p->returns_pointer = current_function_returns_pointer;
   p->needs_context = current_function_needs_context;
   p->calls_setjmp = current_function_calls_setjmp;
   p->calls_longjmp = current_function_calls_longjmp;
   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;
@@ -511,8 +522,9 @@ push_function_context ()
   p->temp_slot_level = temp_slot_level;
   p->fixup_var_refs_queue = 0;
   p->epilogue_delay_list = current_function_epilogue_delay_list;
+  p->args_info = current_function_args_info;
 
-  save_tree_status (p);
+  save_tree_status (p, context);
   save_storage_status (p);
   save_emit_status (p);
   init_emit ();
@@ -524,32 +536,43 @@ push_function_context ()
     (*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;
   current_function_returns_struct = p->returns_struct;
   current_function_returns_pcc_struct = p->returns_pcc_struct;
+  current_function_returns_pointer = p->returns_pointer;
   current_function_needs_context = p->needs_context;
   current_function_calls_setjmp = p->calls_setjmp;
   current_function_calls_longjmp = p->calls_longjmp;
   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;
@@ -577,6 +600,8 @@ 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;
+  current_function_args_info = p->args_info;
 
   restore_tree_status (p);
   restore_storage_status (p);
@@ -602,6 +627,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.  */
 
@@ -609,7 +639,7 @@ pop_function_context ()
    This size counts from zero.  It is not rounded to STACK_BOUNDARY;
    the caller may have to do that.  */
 
-int
+HOST_WIDE_INT
 get_frame_size ()
 {
 #ifdef FRAME_GROWS_DOWNWARD
@@ -666,10 +696,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;
@@ -739,10 +767,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;
@@ -784,6 +810,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)
@@ -812,12 +843,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;
 
@@ -825,6 +859,7 @@ assign_stack_temp (mode, size, keep)
                                         stack_slot_list);
 
              best_p->size = rounded_size;
+             best_p->full_size = rounded_size;
            }
        }
 
@@ -834,18 +869,40 @@ assign_stack_temp (mode, size, keep)
   /* 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);
+      /* 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->addr_taken = 0;
   p->rtl_expr = sequence_rtl_expr;
+
   if (keep == 2)
     {
       p->level = target_temp_slot_level;
@@ -856,9 +913,58 @@ assign_stack_temp (mode, size, keep)
       p->level = temp_slot_level;
       p->keep = keep;
     }
+
+  /* We may be reusing an old slot, so clear any MEM flags that may have been
+     set from before.  */
+  RTX_UNCHANGING_P (p->slot) = 0;
+  MEM_IN_STRUCT_P (p->slot) = 0;
   return p->slot;
 }
+\f
+/* Assign a temporary of given TYPE.
+   KEEP is as for assign_stack_temp.
+   MEMORY_REQUIRED is 1 if the result must be addressable stack memory;
+   it is 0 if a register is OK.
+   DONT_PROMOTE is 1 if we should not promote values in register
+   to wider modes.  */
+
+rtx
+assign_temp (type, keep, memory_required, dont_promote)
+     tree type;
+     int keep;
+     int memory_required;
+     int dont_promote;
+{
+  enum machine_mode mode = TYPE_MODE (type);
+  int unsignedp = TREE_UNSIGNED (type);
+
+  if (mode == BLKmode || memory_required)
+    {
+      int size = int_size_in_bytes (type);
+      rtx tmp;
+
+      /* Unfortunately, we don't yet know how to allocate variable-sized
+        temporaries.  However, sometimes we have a fixed upper limit on
+        the size (which is stored in TYPE_ARRAY_MAX_SIZE) and can use that
+        instead.  This is the case for Chill variable-sized strings.  */
+      if (size == -1 && TREE_CODE (type) == ARRAY_TYPE
+         && TYPE_ARRAY_MAX_SIZE (type) != NULL_TREE
+         && TREE_CODE (TYPE_ARRAY_MAX_SIZE (type)) == INTEGER_CST)
+       size = TREE_INT_CST_LOW (TYPE_ARRAY_MAX_SIZE (type));
+
+      tmp = assign_stack_temp (mode, size, keep);
+      MEM_IN_STRUCT_P (tmp) = AGGREGATE_TYPE_P (type);
+      return tmp;
+    }
+
+#ifndef PROMOTE_FOR_CALL_ONLY
+  if (! dont_promote)
+    mode = promote_mode (type, mode, &unsignedp, 0);
+#endif
 
+  return gen_reg_rtx (mode);
+}
+\f
 /* Combine temporary stack slots which are adjacent on the stack.
 
    This allows for better use of already allocated stack space.  This is only
@@ -882,18 +988,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;
                  }
@@ -934,7 +1040,12 @@ find_temp_slot_from_address (x)
       if (! p->in_use)
        continue;
       else if (XEXP (p->slot, 0) == x
-              || p->address == x)
+              || p->address == x
+              || (GET_CODE (x) == PLUS
+                  && XEXP (x, 0) == virtual_stack_vars_rtx
+                  && GET_CODE (XEXP (x, 1)) == CONST_INT
+                  && INTVAL (XEXP (x, 1)) >= p->base_offset
+                  && INTVAL (XEXP (x, 1)) < p->base_offset + p->full_size))
        return p;
 
       else if (p->address != 0 && GET_CODE (p->address) == EXPR_LIST)
@@ -946,7 +1057,7 @@ find_temp_slot_from_address (x)
   return 0;
 }
       
-/* Indicate that NEW is an alternate way of refering to the temp slot
+/* Indicate that NEW is an alternate way of referring to the temp slot
    that previous was known by OLD.  */
 
 void
@@ -969,10 +1080,33 @@ update_temp_slot_address (old, new)
     }
 }
 
-/* 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
-   them.  Kept slots need not be touched.
+/* 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 than the current level.  If X
+   matched one of our slots, just mark that one.  Otherwise, we can't
+   easily predict which it is, so upgrade all of them.  Kept slots
+   need not be touched.
 
    This is called when an ({...}) construct occurs and a statement
    returns a value in memory.  */
@@ -981,32 +1115,57 @@ 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)
-    return;
+    {
+      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)) != 0)
+  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, but it can contain something whose address was
+     taken.  */
+  if (p == 0 && (GET_CODE (x) != MEM || CONSTANT_P (XEXP (x, 0))))
     {
-      p->level--;
+      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 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;
 
   /* First see if we can find a match.  */
-  p = find_temp_slot_from_address (XEXP (x, 0));
+  if (p == 0)
+    p = find_temp_slot_from_address (XEXP (x, 0));
+
   if (p != 0)
     {
-      p->level--;
+      /* Move everything at our level whose address was taken to our new
+        level in case we used its address.  */
+      struct temp_slot *q;
+
+      if (p->level == temp_slot_level)
+       {
+         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;
     }
 
@@ -1032,14 +1191,14 @@ preserve_rtl_expr_result (x)
   if (x == 0 || GET_CODE (x) != MEM || CONSTANT_P (XEXP (x, 0)))
     return;
 
-  /* If we can find a match, move it to our level.  */
-  for (p = temp_slots; p; p = p->next)
-    if (p->in_use && rtx_equal_p (x, p->slot))
-      {
-       p->level = temp_slot_level;
-       p->rtl_expr = 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;
 }
@@ -1079,6 +1238,21 @@ free_temps_for_rtl_expr (t)
   combine_temp_slots ();
 }
 
+/* Mark all temporaries ever allocated in this functon as not suitable
+   for reuse until the current level is exited.  */
+
+void
+mark_all_temps_used ()
+{
+  struct temp_slot *p;
+
+  for (p = temp_slots; p; p = p->next)
+    {
+      p->in_use = p->keep = 1;
+      p->level = MIN (p->level, temp_slot_level);
+    }
+}
+
 /* Push deeper into the nesting level for stack temporaries.  */
 
 void
@@ -1103,6 +1277,17 @@ pop_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.  */
@@ -1158,28 +1343,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);
@@ -1192,14 +1379,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;
 
@@ -1219,11 +1408,11 @@ 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.  */
@@ -1296,7 +1485,7 @@ fixup_var_refs (var, promoted_mode, unsignedp)
 \f
 /* 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. */
+   value is equal to X.  Allocate a new structure if no such entry exists.  */
 
 static struct fixup_replacement *
 find_fixup_replacement (replacements, x)
@@ -1345,7 +1534,7 @@ fixup_var_refs_insns (var, promoted_mode, unsignedp, insn, toplevel)
 
             If it has a REG_LIBCALL note, delete the REG_LIBCALL
             and REG_RETVAL notes too.  */
-         if (GET_CODE (PATTERN (insn)) == CLOBBER
+         if (GET_CODE (PATTERN (insn)) == CLOBBER
              && XEXP (PATTERN (insn), 0) == var)
            {
              if ((note = find_reg_note (insn, REG_LIBCALL, NULL_RTX)) != 0)
@@ -1394,33 +1583,36 @@ 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
                 and VAR.  */
 
-             if (call_dest != 0 && GET_CODE (insn) == INSN
-                 && reg_mentioned_p (var, PATTERN (insn))
-                 && reg_mentioned_p (call_dest, PATTERN (insn)))
+             if (SMALL_REGISTER_CLASSES)
                {
-                 rtx temp = gen_reg_rtx (GET_MODE (call_dest));
+                 if (call_dest != 0 && GET_CODE (insn) == INSN
+                     && reg_mentioned_p (var, PATTERN (insn))
+                     && reg_mentioned_p (call_dest, PATTERN (insn)))
+                   {
+                     rtx temp = gen_reg_rtx (GET_MODE (call_dest));
 
-                 emit_insn_before (gen_move_insn (temp, call_dest), insn);
+                     emit_insn_before (gen_move_insn (temp, call_dest), insn);
 
-                 PATTERN (insn) = replace_rtx (PATTERN (insn),
-                                               call_dest, temp);
-               }
+                     PATTERN (insn) = replace_rtx (PATTERN (insn),
+                                                   call_dest, temp);
+                   }
              
-             if (GET_CODE (insn) == CALL_INSN
-                 && GET_CODE (PATTERN (insn)) == SET)
-               call_dest = SET_DEST (PATTERN (insn));
-             else if (GET_CODE (insn) == CALL_INSN
-                      && GET_CODE (PATTERN (insn)) == PARALLEL
-                      && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
-               call_dest = SET_DEST (XVECEXP (PATTERN (insn), 0, 0));
-             else
-               call_dest = 0;
+                 if (GET_CODE (insn) == CALL_INSN
+                     && GET_CODE (PATTERN (insn)) == SET)
+                   call_dest = SET_DEST (PATTERN (insn));
+                 else if (GET_CODE (insn) == CALL_INSN
+                          && GET_CODE (PATTERN (insn)) == PARALLEL
+                          && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
+                   call_dest = SET_DEST (XVECEXP (PATTERN (insn), 0, 0));
+                 else
+                   call_dest = 0;
+               }
 #endif
 
              /* See if we have to do anything to INSN now that VAR is in
@@ -1591,8 +1783,20 @@ fixup_var_refs_1 (var, promoted_mode, loc, insn, replacements)
 
          tem = XEXP (x, 0);
          if (GET_CODE (tem) == SUBREG)
-           tem = fixup_memory_subreg (tem, insn, 1);
-         tem = fixup_stack_1 (tem, insn);
+           {
+             if (GET_MODE_BITSIZE (GET_MODE (tem))
+                 > GET_MODE_BITSIZE (GET_MODE (var)))
+               {
+                 replacement = find_fixup_replacement (replacements, var);
+                 if (replacement->new == 0)
+                   replacement->new = gen_reg_rtx (GET_MODE (var));
+                 SUBREG_REG (tem) = replacement->new;
+               }
+             else
+               tem = fixup_memory_subreg (tem, insn, 0);
+           }
+         else
+           tem = fixup_stack_1 (tem, insn);
 
          /* Unless we want to load from memory, get TEM into the proper mode
             for an extract from memory.  This can only be done if the
@@ -1626,10 +1830,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);
 
@@ -1722,8 +1925,24 @@ fixup_var_refs_1 (var, promoted_mode, loc, insn, replacements)
          || GET_CODE (SET_SRC (x)) == ZERO_EXTRACT)
        optimize_bit_field (x, insn, NULL_PTR);
 
+      /* For a paradoxical SUBREG inside a ZERO_EXTRACT, load the object
+        into a register and then store it back out.  */
+      if (GET_CODE (SET_DEST (x)) == ZERO_EXTRACT
+         && GET_CODE (XEXP (SET_DEST (x), 0)) == SUBREG
+         && SUBREG_REG (XEXP (SET_DEST (x), 0)) == var
+         && (GET_MODE_SIZE (GET_MODE (XEXP (SET_DEST (x), 0)))
+             > GET_MODE_SIZE (GET_MODE (var))))
+       {
+         replacement = find_fixup_replacement (replacements, var);
+         if (replacement->new == 0)
+           replacement->new = gen_reg_rtx (GET_MODE (var));
+
+         SUBREG_REG (XEXP (SET_DEST (x), 0)) = replacement->new;
+         emit_insn_after (gen_move_insn (var, replacement->new), insn);
+       }
+
       /* If SET_DEST is now a paradoxical SUBREG, put the result of this
-        insn into a pseudo and store the low part of the pseudo into VAR. */
+        insn into a pseudo and store the low part of the pseudo into VAR.  */
       if (GET_CODE (SET_DEST (x)) == SUBREG
          && SUBREG_REG (SET_DEST (x)) == var
          && (GET_MODE_SIZE (GET_MODE (SET_DEST (x)))
@@ -1777,7 +1996,7 @@ fixup_var_refs_1 (var, promoted_mode, loc, insn, replacements)
               This was legitimate when the MEM was a REG.  */
            if (GET_CODE (tem) == SUBREG
                && SUBREG_REG (tem) == var)
-             tem = fixup_memory_subreg (tem, insn, 1);
+             tem = fixup_memory_subreg (tem, insn, 0);
            else
              tem = fixup_stack_1 (tem, insn);
 
@@ -1799,10 +2018,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);
 
@@ -1862,7 +2080,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;
 
@@ -1907,7 +2126,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;
 
@@ -1953,13 +2173,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),
@@ -1993,7 +2214,7 @@ fixup_var_refs_1 (var, promoted_mode, loc, insn, replacements)
    If any insns must be emitted to compute NEWADDR, put them before INSN.
 
    UNCRITICAL nonzero means accept paradoxical subregs.
-   This is used for subregs found inside of ZERO_EXTRACTs and in REG_NOTES. */
+   This is used for subregs found inside REG_NOTES.  */
 
 static rtx
 fixup_memory_subreg (x, insn, uncritical)
@@ -2011,10 +2232,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.  */
@@ -2191,26 +2411,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.  */
@@ -2243,7 +2468,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);
@@ -2385,7 +2612,7 @@ instantiate_decls (fndecl, valid_only)
 {
   tree decl;
 
-  if (DECL_INLINE (fndecl))
+  if (DECL_SAVED_INSNS (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
@@ -2395,16 +2622,20 @@ instantiate_decls (fndecl, valid_only)
   /* Process all parameters of the function.  */
   for (decl = DECL_ARGUMENTS (fndecl); decl; decl = TREE_CHAIN (decl))
     {
-      instantiate_decl (DECL_RTL (decl), int_size_in_bytes (TREE_TYPE (decl)),
-                       valid_only);    
-      instantiate_decl (DECL_INCOMING_RTL (decl),
-                       int_size_in_bytes (TREE_TYPE (decl)), valid_only);
+      int size = int_size_in_bytes (TREE_TYPE (decl));
+      instantiate_decl (DECL_RTL (decl), size, valid_only);    
+
+      /* If the parameter was promoted, then the incoming RTL mode may be
+        larger than the declared type size.  We must use the larger of
+        the two sizes.  */
+      size = MAX (GET_MODE_SIZE (GET_MODE (DECL_INCOMING_RTL (decl))), size);
+      instantiate_decl (DECL_INCOMING_RTL (decl), size, valid_only);
     }
 
-  /* Now process all variables defined in the function or its subblocks. */
+  /* 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,28 +2702,28 @@ instantiate_decl (x, size, valid_only)
 
   instantiate_virtual_regs_1 (&addr, NULL_RTX, 0);
 
-  if (! valid_only)
-    return;
-
-  /* Now verify that the resulting address is valid for every integer or
-     floating-point mode up to and including SIZE bytes long.  We do this
-     since the object might be accessed in any mode and frame addresses
-     are shared.  */
-
-  for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
-       mode != VOIDmode && GET_MODE_SIZE (mode) <= size;
-       mode = GET_MODE_WIDER_MODE (mode))
-    if (! memory_address_p (mode, addr))
-      return;
+  if (valid_only)
+    {
+      /* Now verify that the resulting address is valid for every integer or
+        floating-point mode up to and including SIZE bytes long.  We do this
+        since the object might be accessed in any mode and frame addresses
+        are shared.  */
+
+      for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+          mode != VOIDmode && GET_MODE_SIZE (mode) <= size;
+          mode = GET_MODE_WIDER_MODE (mode))
+       if (! memory_address_p (mode, addr))
+         return;
 
-  for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
-       mode != VOIDmode && GET_MODE_SIZE (mode) <= size;
-       mode = GET_MODE_WIDER_MODE (mode))
-    if (! memory_address_p (mode, addr))
-      return;
+      for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
+          mode != VOIDmode && GET_MODE_SIZE (mode) <= size;
+          mode = GET_MODE_WIDER_MODE (mode))
+       if (! memory_address_p (mode, addr))
+         return;
+    }
 
-  /* Otherwise, put back the address, now that we have updated it and we
-     know it is valid.  */
+  /* Put back the address now that we have updated it and we either know
+     it is valid or we don't care whether it is valid.  */
 
   XEXP (x, 0) = addr;
 }
@@ -2600,7 +2831,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)
@@ -2647,18 +2878,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)
                {
@@ -2669,15 +2908,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);
@@ -2715,7 +2955,7 @@ instantiate_virtual_regs_1 (loc, object, extra_insns)
 
     case MEM:
       /* Most cases of MEM that convert to valid addresses have already been
-        handled by our scan of regno_reg_rtx.  The only special handling we
+        handled by our scan of decls.  The only special handling we
         need here is to make a copy of the rtx to ensure it isn't being
         shared if we have to change it to a pseudo. 
 
@@ -2773,7 +3013,9 @@ instantiate_virtual_regs_1 (loc, object, extra_insns)
             ??? Also note that this can still lose if OBJECT is an insn that
             has less restrictions on an address that some other insn.
             In that case, we will modify the shared address.  This case
-            doesn't seem very likely, though.  */
+            doesn't seem very likely, though.  One case where this could
+            happen is in the case of a USE or CLOBBER reference, but we
+            take care of that below.  */
 
          if (instantiate_virtual_regs_1 (&XEXP (x, 0),
                                          object ? object : x, 0))
@@ -2786,8 +3028,6 @@ instantiate_virtual_regs_1 (loc, object, extra_insns)
        }
 
       /* Fall through to generic unary operation case.  */
-    case USE:
-    case CLOBBER:
     case SUBREG:
     case STRICT_LOW_PART:
     case NEG:          case NOT:
@@ -2804,6 +3044,23 @@ instantiate_virtual_regs_1 (loc, object, extra_insns)
       loc = &XEXP (x, 0);
       goto restart;
 
+    case USE:
+    case CLOBBER:
+      /* If the operand is a MEM, see if the change is a valid MEM.  If not,
+        go ahead and make the invalid one, but do it to a copy.  For a REG,
+        just make the recursive call, since there's no chance of a problem. */
+
+      if ((GET_CODE (XEXP (x, 0)) == MEM
+          && instantiate_virtual_regs_1 (&XEXP (XEXP (x, 0), 0), XEXP (x, 0),
+                                         0))
+         || (GET_CODE (XEXP (x, 0)) == REG
+             && instantiate_virtual_regs_1 (&XEXP (x, 0), object, 0)))
+       return 1;
+
+      XEXP (x, 0) = copy_rtx (XEXP (x, 0));
+      loc = &XEXP (x, 0);
+      goto restart;
+
     case REG:
       /* Try to replace with a PLUS.  If that doesn't work, compute the sum
         in front of this insn and substitute the temporary.  */
@@ -2871,7 +3128,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)))
@@ -2989,11 +3264,21 @@ aggregate_value_p (exp)
 
   if (RETURN_IN_MEMORY (type))
     return 1;
+  /* Types that are TREE_ADDRESSABLE must be contructed in memory,
+     and thus can't be returned in registers.  */
+  if (TREE_ADDRESSABLE (type))
+    return 1;
   if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type))
     return 1;
   /* Make sure we have suitable call-clobbered regs to return
      the value in; if not, we must return it in memory.  */
   reg = hard_function_value (type, 0);
+
+  /* If we have something other than a REG (e.g. a PARALLEL), then assume
+     it is OK.  */
+  if (GET_CODE (reg) != REG)
+    return 0;
+
   regno = REGNO (reg);
   nregs = HARD_REGNO_NREGS (regno, TYPE_MODE (type));
   for (i = 0; i < nregs; i++)
@@ -3022,7 +3307,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.  */
@@ -3037,9 +3323,6 @@ assign_parms (fndecl, second_time)
   int nparmregs = list_length (fnargs) + LAST_VIRTUAL_REGISTER + 1;
   int varargs_setup = 0;
   rtx conversion_insns = 0;
-  /* FUNCTION_ARG may look at this variable.  Since this is not
-     expanding a call it will always be zero in this function.  */
-  int current_call_is_indirect = 0;
 
   /* Nonzero if the last arg is named `__builtin_va_alist',
      which is used on some machines for old-fashioned non-ANSI varargs.h;
@@ -3054,12 +3337,14 @@ assign_parms (fndecl, second_time)
 
   /* Nonzero if function takes extra anonymous args.
      This means the last named arg must be on the stack
-     right before the anonymous ones. */
+     right before the anonymous ones.  */
   int stdarg
     = (TYPE_ARG_TYPES (fntype) != 0
        && (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
@@ -3084,7 +3369,7 @@ assign_parms (fndecl, second_time)
       && ! current_function_returns_pcc_struct
       && struct_value_incoming_rtx == 0)
     {
-      tree type = build_pointer_type (fntype);
+      tree type = build_pointer_type (TREE_TYPE (fntype));
 
       function_result_decl = build_decl (PARM_DECL, NULL_TREE, type);
 
@@ -3099,7 +3384,7 @@ assign_parms (fndecl, second_time)
 #ifdef INIT_CUMULATIVE_INCOMING_ARGS
   INIT_CUMULATIVE_INCOMING_ARGS (args_so_far, fntype, NULL_RTX);
 #else
-  INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX);
+  INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, 0);
 #endif
 
   /* We haven't yet found an argument that we must push and pretend the
@@ -3112,7 +3397,9 @@ assign_parms (fndecl, second_time)
       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.  */
@@ -3140,7 +3427,7 @@ assign_parms (fndecl, second_time)
       /* 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.  */
@@ -3164,14 +3451,14 @@ assign_parms (fndecl, second_time)
 
       if ((TREE_CODE (TYPE_SIZE (passed_type)) != INTEGER_CST
           && contains_placeholder_p (TYPE_SIZE (passed_type)))
-         || TYPE_NEEDS_CONSTRUCTING (passed_type)
+         || TREE_ADDRESSABLE (passed_type)
 #ifdef FUNCTION_ARG_PASS_BY_REFERENCE
          || 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;
        }
@@ -3193,8 +3480,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
@@ -3209,7 +3496,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;
@@ -3229,17 +3516,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
@@ -3251,20 +3538,22 @@ 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));
 
          /* If this is a memory ref that contains aggregate components,
-            mark it as such for cse and loop optimize.  */
+            mark it as such for cse and loop optimize.  Likewise if it
+            is readonly.  */
          MEM_IN_STRUCT_P (stack_parm) = aggregate;
+         RTX_UNCHANGING_P (stack_parm) = TREE_READONLY (parm);
        }
 
       /* 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
@@ -3278,7 +3567,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)
@@ -3289,9 +3578,17 @@ assign_parms (fndecl, second_time)
                   * (PARM_BOUNDARY / BITS_PER_UNIT));
 
              if (! second_time)
-               move_block_from_reg (REGNO (entry_parm),
-                                    validize_mem (stack_parm), nregs,
-                                    int_size_in_bytes (TREE_TYPE (parm)));
+               {
+                 /* Handle calls that pass values in multiple non-contiguous
+                    locations.  The Irix 6 ABI has examples of this.  */
+                 if (GET_CODE (entry_parm) == PARALLEL)
+                   emit_group_store (validize_mem (stack_parm),
+                                        entry_parm);
+                 else
+                   move_block_from_reg (REGNO (entry_parm),
+                                        validize_mem (stack_parm), nregs,
+                                        int_size_in_bytes (TREE_TYPE (parm)));
+               }
              entry_parm = stack_parm;
            }
        }
@@ -3318,7 +3615,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
@@ -3335,10 +3632,10 @@ 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. */
+      /* If this is our second time through, we are done with this parm.  */
       if (second_time)
        continue;
 
@@ -3347,7 +3644,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;
@@ -3371,11 +3668,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)
@@ -3391,6 +3687,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
@@ -3405,13 +3712,17 @@ assign_parms (fndecl, second_time)
 
         Set DECL_RTL to that place.  */
 
-      if (nominal_mode == BLKmode)
+      if (nominal_mode == BLKmode || GET_CODE (entry_parm) == PARALLEL)
        {
-         /* If a BLKmode arrives in registers, copy it to a stack slot.  */
-         if (GET_CODE (entry_parm) == REG)
+         /* If a BLKmode arrives in registers, copy it to a stack slot.
+            Handle calls that pass values in multiple non-contiguous
+            locations.  The Irix 6 ABI has examples of this.  */
+         if (GET_CODE (entry_parm) == REG
+             || GET_CODE (entry_parm) == PARALLEL)
            {
-             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
@@ -3424,9 +3735,11 @@ 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;
                }
 
@@ -3436,10 +3749,15 @@ assign_parms (fndecl, second_time)
              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,
-                                  int_size_in_bytes (TREE_TYPE (parm)));
+             /* Handle calls that pass values in multiple non-contiguous
+                locations.  The Irix 6 ABI has examples of this.  */
+             if (GET_CODE (entry_parm) == PARALLEL)
+               emit_group_store (validize_mem (stack_parm), entry_parm);
+             else
+               move_block_from_reg (REGNO (entry_parm),
+                                    validize_mem (stack_parm),
+                                    size_stored / UNITS_PER_WORD,
+                                    int_size_in_bytes (TREE_TYPE (parm)));
            }
          DECL_RTL (parm) = stack_parm;
        }
@@ -3463,26 +3781,35 @@ assign_parms (fndecl, second_time)
          int regno, regnoi, regnor;
 
          unsignedp = TREE_UNSIGNED (TREE_TYPE (parm));
-         nominal_mode = promote_mode (TREE_TYPE (parm), nominal_mode,
-                                      &unsignedp, 1);
 
-         parmreg = gen_reg_rtx (nominal_mode);
-         REG_USERVAR_P (parmreg) = 1;
+         promoted_nominal_mode
+           = promote_mode (TREE_TYPE (parm), nominal_mode, &unsignedp, 0);
+
+         parmreg = gen_reg_rtx (promoted_nominal_mode);
+         mark_user_reg (parmreg);
 
          /* 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
@@ -3500,8 +3827,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
@@ -3523,7 +3854,7 @@ 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;
+             mark_user_reg (parmreg);
              emit_move_insn (parmreg, DECL_RTL (parm));
              DECL_RTL (parm) = parmreg;
              /* STACK_PARM is the pointer, not the parm, and PARMREG is
@@ -3543,7 +3874,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);
@@ -3563,10 +3895,12 @@ assign_parms (fndecl, second_time)
              else
                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 */
@@ -3626,49 +3960,56 @@ 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
-             && ! conversion_insns
-             && GET_CODE (entry_parm) == MEM
-             && entry_parm == stack_parm
+             && ! did_conversion
+             && stack_parm != 0
+             && GET_CODE (stack_parm) == MEM
              && stack_offset.var == 0
              && reg_mentioned_p (virtual_incoming_args_rtx,
-                                 XEXP (entry_parm, 0)))
+                                 XEXP (stack_parm, 0)))
            {
              rtx linsn = get_last_insn ();
+             rtx sinsn, set;
 
              /* 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
+               /* Scan backwards for the set of the real and
+                  imaginary parts.  */
+               for (sinsn = linsn; sinsn != 0;
+                    sinsn = prev_nonnote_insn (sinsn))
+                 {
+                   set = single_set (sinsn);
+                   if (set != 0
+                       && SET_DEST (set) == regno_reg_rtx [regnoi])
+                     REG_NOTES (sinsn)
+                       = gen_rtx (EXPR_LIST, REG_EQUIV,
+                                  parm_reg_stack_loc[regnoi],
+                                  REG_NOTES (sinsn));
+                   else if (set != 0
+                            && SET_DEST (set) == regno_reg_rtx [regnor])
+                     REG_NOTES (sinsn)
+                       = gen_rtx (EXPR_LIST, REG_EQUIV,
+                                  parm_reg_stack_loc[regnor],
+                                  REG_NOTES (sinsn));
+                 }
+             else if ((set = single_set (linsn)) != 0
+                      && SET_DEST (set) == parmreg)
                REG_NOTES (linsn)
-                = gen_rtx (EXPR_LIST, REG_EQUIV,
-                           entry_parm, REG_NOTES (linsn));
+                 = gen_rtx (EXPR_LIST, REG_EQUIV,
+                            stack_parm, REG_NOTES (linsn));
            }
 
          /* For pointer data type, suggest pointer register.  */
          if (TREE_CODE (TREE_TYPE (parm)) == POINTER_TYPE)
-           mark_reg_pointer (parmreg);
+           mark_reg_pointer (parmreg,
+                             (TYPE_ALIGN (TREE_TYPE (TREE_TYPE (parm)))
+                              / BITS_PER_UNIT));
        }
       else
        {
          /* 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));
@@ -3679,6 +4020,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 ();
            }
 
@@ -3694,7 +4036,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),
@@ -3769,7 +4111,7 @@ 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
@@ -3805,7 +4147,8 @@ promoted_input_arg (regno, pmode, punsignedp)
   for (arg = DECL_ARGUMENTS (current_function_decl); arg;
        arg = TREE_CHAIN (arg))
     if (GET_CODE (DECL_INCOMING_RTL (arg)) == REG
-       && REGNO (DECL_INCOMING_RTL (arg)) == regno)
+       && REGNO (DECL_INCOMING_RTL (arg)) == regno
+       && 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));
@@ -3941,15 +4284,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 */
 }
@@ -4064,8 +4411,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);
@@ -4141,9 +4487,10 @@ lookup_static_chain (decl)
   tree context = decl_function_context (decl);
   tree link;
 
-  if (context == 0)
+  if (context == 0
+      || (TREE_CODE (decl) == FUNCTION_DECL && DECL_NO_STATIC_CHAIN (decl)))
     return 0;
-  
+
   /* We treat inline_function_decl as an alias for the current function
      because that is the inline function whose vars, types, etc.
      are being merged into the current function.
@@ -4261,7 +4608,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)
@@ -4289,7 +4638,8 @@ trampoline_address (function)
   /* If rounding needed, allocate extra space
      to ensure we have TRAMPOLINE_SIZE bytes left after rounding up.  */
 #ifdef TRAMPOLINE_ALIGNMENT
-#define TRAMPOLINE_REAL_SIZE (TRAMPOLINE_SIZE + TRAMPOLINE_ALIGNMENT - 1)
+#define TRAMPOLINE_REAL_SIZE \
+  (TRAMPOLINE_SIZE + (TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT) - 1)
 #else
 #define TRAMPOLINE_REAL_SIZE (TRAMPOLINE_SIZE)
 #endif
@@ -4337,10 +4687,10 @@ round_trampoline_addr (tramp)
   /* Round address up to desired boundary.  */
   rtx temp = gen_reg_rtx (Pmode);
   temp = expand_binop (Pmode, add_optab, tramp,
-                      GEN_INT (TRAMPOLINE_ALIGNMENT - 1),
+                      GEN_INT (TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT - 1),
                       temp, 0, OPTAB_LIB_WIDEN);
   tramp = expand_binop (Pmode, and_optab, temp,
-                       GEN_INT (- TRAMPOLINE_ALIGNMENT),
+                       GEN_INT (- TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT),
                        temp, 0, OPTAB_LIB_WIDEN);
 #endif
   return tramp;
@@ -4351,33 +4701,34 @@ round_trampoline_addr (tramp)
    duplicate portions of the RTL code.  Call identify_blocks before
    changing the RTL, and call reorder_blocks after.  */
 
-/* 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)
@@ -4395,6 +4746,9 @@ identify_blocks (top_block, insns)
          }
       }
 
+  if (n_blocks != next_block_number)
+    abort ();
+
   return block_vector;
 }
 
@@ -4405,19 +4759,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)
@@ -4445,6 +4800,8 @@ reorder_blocks (block_vector, top_block, insns)
          }
       }
 
+  BLOCK_SUBBLOCKS (current_block)
+    = blocks_nreverse (BLOCK_SUBBLOCKS (current_block));
   return current_block;
 }
 
@@ -4465,31 +4822,37 @@ 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. */
+/* Build bytecode call descriptor for function SUBR.  */
 
 rtx
 bc_build_calldesc (subr)
@@ -4543,8 +4906,6 @@ init_function_start (subr, filename, line)
      char *filename;
      int line;
 {
-  char *junk;
-
   if (output_bytecode)
     {
       this_function_decl = subr;
@@ -4592,12 +4953,13 @@ init_function_start (subr, filename, line)
 
   init_const_rtx_hash_table ();
 
-  current_function_name = (*decl_printable_name) (subr, &junk);
+  current_function_name = (*decl_printable_name) (subr, 2);
 
   /* Nonzero if this is a nested function that uses a static chain.  */
 
   current_function_needs_context
-    = (decl_function_context (current_function_decl) != 0);
+    = (decl_function_context (current_function_decl) != 0
+       && ! DECL_NO_STATIC_CHAIN (current_function_decl));
 
   /* Set if a call to setjmp is seen.  */
   current_function_calls_setjmp = 0;
@@ -4632,10 +4994,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;
-  target_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++;
@@ -4648,9 +5008,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);
@@ -4676,7 +5033,7 @@ init_function_start (subr, filename, line)
     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.  */
@@ -4688,8 +5045,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
@@ -4725,7 +5083,7 @@ expand_main_function ()
 extern struct obstack permanent_obstack;
 
 /* Expand start of bytecode function. See comment at
-   expand_function_start below for details. */
+   expand_function_start below for details.  */
 
 void
 bc_expand_function_start (subr, parms_have_cleanups)
@@ -4755,13 +5113,13 @@ bc_expand_function_start (subr, parms_have_cleanups)
        }
       else
        {
-         /* Variable-sized objects are pointers to their storage. */
+         /* Variable-sized objects are pointers to their storage.  */
          DECL_RTL (thisarg) = bc_gen_rtx ((char *) 0, argsz, (struct bc_label *) 0);
          argsz += POINTER_SIZE;
        }
     }
 
-  bc_begin_function (bc_xstrdup (IDENTIFIER_POINTER (DECL_NAME (subr))));
+  bc_begin_function (xstrdup (IDENTIFIER_POINTER (DECL_NAME (subr))));
 
   ASM_GENERATE_INTERNAL_LABEL (label, "LX", nlab);
 
@@ -4774,7 +5132,7 @@ bc_expand_function_start (subr, parms_have_cleanups)
 
 
 /* Expand end of bytecode function. See details the comment of
-   expand_function_end(), below. */
+   expand_function_end(), below.  */
 
 void
 bc_expand_function_end ()
@@ -4785,7 +5143,7 @@ bc_expand_function_end ()
 
   /* Emit any fixup code. This must be done before the call to
      to BC_END_FUNCTION (), since that will cause the bytecode
-     segment to be finished off and closed. */
+     segment to be finished off and closed.  */
 
   expand_fixups (NULL_RTX);
 
@@ -4838,7 +5196,8 @@ expand_function_start (subr, parms_have_cleanups)
 #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)
+      if (! SMALL_REGISTER_CLASSES
+         || GET_CODE (static_chain_incoming_rtx) == REG)
 #endif
         emit_move_insn (last_ptr, static_chain_incoming_rtx);
     }
@@ -4952,7 +5311,7 @@ expand_function_start (subr, parms_have_cleanups)
   /* 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 (SMALL_REGISTER_CLASSES && current_function_needs_context)
       if (GET_CODE (static_chain_incoming_rtx) != REG)
         emit_move_insn (last_ptr, static_chain_incoming_rtx);
 #endif
@@ -4978,48 +5337,53 @@ expand_function_start (subr, parms_have_cleanups)
        use_variable (current_function_internal_arg_pointer);
     }
 
-  /* Fetch static chain values for containing functions.  */
-  tem = decl_function_context (current_function_decl);
-  /* If not doing stupid register allocation copy the static chain
-     pointer into a psuedo.  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)
+  context_display = 0;
+  if (current_function_needs_context)
     {
+      /* Fetch static chain values for containing functions.  */
+      tem = decl_function_context (current_function_decl);
+      /* 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)
+       {
 #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);
+         /* 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 (SMALL_REGISTER_CLASSES
+             && 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);
-    }
+         last_ptr = copy_to_reg (static_chain_incoming_rtx);
+       }
 
-  context_display = 0;
-  while (tem)
-    {
-      tree rtlexp = make_node (RTL_EXPR);
+      while (tem)
+       {
+         tree rtlexp = make_node (RTL_EXPR);
 
-      RTL_EXPR_RTL (rtlexp) = last_ptr;
-      context_display = tree_cons (tem, rtlexp, context_display);
-      tem = decl_function_context (tem);
-      if (tem == 0)
-       break;
-      /* Chain thru stack frames, assuming pointer to next lexical frame
-        is found at the place we always store it.  */
+         RTL_EXPR_RTL (rtlexp) = last_ptr;
+         context_display = tree_cons (tem, rtlexp, context_display);
+         tem = decl_function_context (tem);
+         if (tem == 0)
+           break;
+         /* Chain thru stack frames, assuming pointer to next lexical frame
+            is found at the place we always store it.  */
 #ifdef FRAME_GROWS_DOWNWARD
-      last_ptr = plus_constant (last_ptr, - GET_MODE_SIZE (Pmode));
+         last_ptr = plus_constant (last_ptr, - GET_MODE_SIZE (Pmode));
 #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);
+         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
@@ -5029,7 +5393,12 @@ expand_function_start (subr, parms_have_cleanups)
 
   /* Evaluate now the sizes of any types declared among the arguments.  */
   for (tem = nreverse (get_pending_sizes ()); tem; tem = TREE_CHAIN (tem))
-    expand_expr (TREE_VALUE (tem), const0_rtx, VOIDmode, 0);
+    {
+      expand_expr (TREE_VALUE (tem), const0_rtx, VOIDmode, 0);
+      /* Flush the queue in case this parameter declaration has
+        side-effects.  */
+      emit_queue ();
+    }
 
   /* Make sure there is a line number after the function entry setup code.  */
   force_next_line_note ();
@@ -5050,7 +5419,9 @@ expand_function_end (filename, line, end_bindings)
   register int i;
   tree link;
 
+#ifdef TRAMPOLINE_TEMPLATE
   static rtx initial_trampoline;
+#endif
 
   if (output_bytecode)
     {
@@ -5063,7 +5434,9 @@ expand_function_end (filename, line, end_bindings)
      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
@@ -5081,8 +5454,10 @@ expand_function_end (filename, line, end_bindings)
       tree function = TREE_PURPOSE (link);
       rtx context = lookup_static_chain (function);
       rtx tramp = RTL_EXPR_RTL (TREE_VALUE (link));
+      rtx blktramp;
       rtx seq;
 
+#ifdef TRAMPOLINE_TEMPLATE
       /* First make sure this compilation has a template for
         initializing trampolines.  */
       if (initial_trampoline == 0)
@@ -5092,15 +5467,18 @@ expand_function_end (filename, line, end_bindings)
            = gen_rtx (MEM, BLKmode, assemble_trampoline_template ());
          resume_temporary_allocation ();
        }
+#endif
 
       /* Generate insns to initialize the trampoline.  */
       start_sequence ();
-      tramp = change_address (initial_trampoline, BLKmode,
-                             round_trampoline_addr (XEXP (tramp, 0)));
-      emit_block_move (tramp, initial_trampoline, GEN_INT (TRAMPOLINE_SIZE),
-                      FUNCTION_BOUNDARY / BITS_PER_UNIT);
-      INITIALIZE_TRAMPOLINE (XEXP (tramp, 0),
-                            XEXP (DECL_RTL (function), 0), context);
+      tramp = round_trampoline_addr (XEXP (tramp, 0));
+#ifdef TRAMPOLINE_TEMPLATE
+      blktramp = change_address (initial_trampoline, BLKmode, tramp);
+      emit_block_move (blktramp, initial_trampoline,
+                      GEN_INT (TRAMPOLINE_SIZE),
+                      TRAMPOLINE_ALIGNMENT / BITS_PER_UNIT);
+#endif
+      INITIALIZE_TRAMPOLINE (tramp, XEXP (DECL_RTL (function), 0), context);
       seq = get_insns ();
       end_sequence ();
 
@@ -5108,18 +5486,17 @@ expand_function_end (filename, line, end_bindings)
       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)
@@ -5162,6 +5539,14 @@ expand_function_end (filename, line, end_bindings)
      without returning a value.  */
   emit_note (NULL_PTR, NOTE_INSN_FUNCTION_END);
 
+  /* Must mark the last line number note in the function, so that the test
+     coverage code can avoid counting the last line twice.  This just tells
+     the code to ignore the immediately following line note, since there
+     already exists a copy of this note somewhere above.  This line number
+     note is still needed for debugging though, so we can't delete it.  */
+  if (flag_test_coverage)
+    emit_note (NULL_PTR, NOTE_REPEATED_LINE_NUMBER);
+
   /* Output a linenumber for the end of the function.
      SDB depends on this.  */
   emit_line_note_force (filename, line);
@@ -5212,6 +5597,11 @@ expand_function_end (filename, line, end_bindings)
                          current_function_decl);
 #endif
       REG_FUNCTION_VALUE_P (real_decl_result) = 1;
+      /* If this is a BLKmode structure being returned in registers, then use
+        the mode computed in expand_return.  */
+      if (GET_MODE (real_decl_result) == BLKmode)
+       PUT_MODE (real_decl_result,
+                 GET_MODE (DECL_RTL (DECL_RESULT (current_function_decl))));
       emit_move_insn (real_decl_result,
                      DECL_RTL (DECL_RESULT (current_function_decl)));
       emit_insn (gen_rtx (USE, VOIDmode, real_decl_result));
@@ -5327,7 +5717,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.  */