OSDN Git Service

(fold_rtx, case PLUS): When seeing if negative of constant is around,
[pf3gnuchains/gcc-fork.git] / gcc / function.c
index 6fd2bb7..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-94, 1995 Free Software Foundation, Inc.
+   Copyright (C) 1987, 88, 89, 91-96, 1997 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -39,12 +39,11 @@ Boston, MA 02111-1307, 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"
@@ -57,11 +56,16 @@ Boston, MA 02111-1307, 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 neither. */
+   must define both, or neither.  */
 #ifndef NAME__MAIN
 #define NAME__MAIN "__main"
 #define SYMBOL__MAIN __main
@@ -154,7 +158,7 @@ int current_function_args_size;
 int current_function_pretend_args_size;
 
 /* # of bytes of outgoing arguments.  If ACCUMULATE_OUTGOING_ARGS is
-   defined, the needed space is pushed by the prologue. */
+   defined, the needed space is pushed by the prologue.  */
 
 int current_function_outgoing_args_size;
 
@@ -263,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
@@ -300,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;
@@ -314,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
@@ -323,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.
@@ -350,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.  */
@@ -488,6 +481,7 @@ push_function_context_to (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;
@@ -528,6 +522,7 @@ push_function_context_to (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, context);
   save_storage_status (p);
@@ -566,6 +561,7 @@ pop_function_context_from (context)
   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;
@@ -605,6 +601,7 @@ pop_function_context_from (context)
   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);
@@ -642,7 +639,7 @@ void 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
@@ -916,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
@@ -994,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)
@@ -1051,10 +1102,11 @@ mark_temp_addr_taken (x)
     p->addr_taken = 1;
 }
 
-/* If X could be a reference to a temporary slot, mark that slot as belonging
-   to the to one level higher.  If X matched one of our slots, just mark that
-   one.  Otherwise, we can't easily predict which it is, so upgrade all of
-   them.  Kept slots need not be touched.
+/* 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.  */
@@ -1105,12 +1157,15 @@ preserve_temp_slots (x)
         level in case we used its address.  */
       struct temp_slot *q;
 
-      for (q = temp_slots; q; q = q->next)
-       if (q != p && q->addr_taken && q->level == p->level)
-         q->level--;
+      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;
+         p->level--;
+         p->addr_taken = 0;
+       }
       return;
     }
 
@@ -1183,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
@@ -1415,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)
@@ -1519,27 +1589,30 @@ fixup_var_refs_insns (var, promoted_mode, unsignedp, insn, toplevel)
                 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
@@ -1710,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
@@ -1840,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)))
@@ -1895,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);
 
@@ -1979,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;
 
@@ -2024,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;
 
@@ -2111,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)
@@ -2509,7 +2612,7 @@ instantiate_decls (fndecl, valid_only)
 {
   tree decl;
 
-  if (DECL_INLINE (fndecl) || DECL_DEFER_OUTPUT (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
@@ -2519,13 +2622,17 @@ 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) || DECL_DEFER_OUTPUT (fndecl))
@@ -2595,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;
 }
@@ -2784,7 +2891,7 @@ instantiate_virtual_regs_1 (loc, object, extra_insns)
             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). */
+            changes and then call apply_change_group).  */
 
          old = XEXP (x, 0);
          if (offset == 0
@@ -2848,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. 
 
@@ -2906,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))
@@ -2919,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:
@@ -2937,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.  */
@@ -3140,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++)
@@ -3189,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;
@@ -3206,7 +3337,7 @@ 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)))
@@ -3238,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);
 
@@ -3253,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
@@ -3414,8 +3545,10 @@ assign_parms (fndecl, second_time)
                                           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,
@@ -3445,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;
            }
        }
@@ -3494,7 +3635,7 @@ assign_parms (fndecl, second_time)
       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;
 
@@ -3571,10 +3712,13 @@ 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)),
@@ -3605,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;
        }
@@ -3637,7 +3786,7 @@ assign_parms (fndecl, second_time)
            = promote_mode (TREE_TYPE (parm), nominal_mode, &unsignedp, 0);
 
          parmreg = gen_reg_rtx (promoted_nominal_mode);
-         REG_USERVAR_P (parmreg) = 1;
+         mark_user_reg (parmreg);
 
          /* If this was an item that we received a pointer to, set DECL_RTL
             appropriately.  */
@@ -3705,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
@@ -3812,41 +3961,48 @@ assign_parms (fndecl, second_time)
             as we make here would screw up life analysis for it.  */
          if (nominal_mode == passed_mode
              && ! did_conversion
-             && GET_CODE (entry_parm) == MEM
-             && entry_parm == stack_parm
+             && stack_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
        {
@@ -4331,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.
@@ -4481,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
@@ -4529,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;
@@ -4694,7 +4852,7 @@ all_blocks (block, vector)
   return n_blocks;
 }
 \f
-/* Build bytecode call descriptor for function SUBR. */
+/* Build bytecode call descriptor for function SUBR.  */
 
 rtx
 bc_build_calldesc (subr)
@@ -4748,8 +4906,6 @@ init_function_start (subr, filename, line)
      char *filename;
      int line;
 {
-  char *junk;
-
   if (output_bytecode)
     {
       this_function_decl = subr;
@@ -4797,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;
@@ -4926,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)
@@ -4956,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);
 
@@ -4975,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 ()
@@ -4986,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);
 
@@ -5039,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);
     }
@@ -5153,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
@@ -5179,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 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)
+  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
@@ -5230,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 ();
@@ -5251,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)
     {
@@ -5284,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)
@@ -5295,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 ();
 
@@ -5364,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);
@@ -5414,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));