OSDN Git Service

(calls_alloca): Don't look within DECL_INITIAL if it's 0.
[pf3gnuchains/gcc-fork.git] / gcc / calls.c
index 8541d16..4d6c848 100644 (file)
@@ -50,10 +50,16 @@ struct arg_data
      EXPR_LIST if the arg is to be copied into multiple different
      registers.  */
   rtx reg;
+  /* If REG was promoted from the actual mode of the argument expression,
+     indicates whether the promotion is sign- or zero-extended.  */
+  int unsignedp;
   /* Number of registers to use.  0 means put the whole arg in registers.
      Also 0 if not passed in registers.  */
   int partial;
-  /* Non-zero if argument must be passed on stack.  */
+  /* Non-zero if argument must be passed on stack.
+     Note that some arguments may be passed on the stack
+     even though pass_on_stack is zero, just because FUNCTION_ARG says so.
+     pass_on_stack identifies arguments that *cannot* go in registers.  */
   int pass_on_stack;
   /* Offset of this argument from beginning of stack-args.  */
   struct args_size offset;
@@ -87,6 +93,13 @@ static char *stack_usage_map;
 
 /* Size of STACK_USAGE_MAP.  */
 static int highest_outgoing_arg_in_use;
+
+/* stack_arg_under_construction is nonzero when an argument may be
+   initialized with a constructor call (including a C function that
+   returns a BLKmode struct) and expand_call must take special action
+   to make sure the object being constructed does not overlap the
+   argument list for the constructor call.  */
+int stack_arg_under_construction;
 #endif
 
 static void store_one_arg ();
@@ -104,7 +117,8 @@ calls_alloca (exp)
 
   /* Only expressions and references can contain calls.  */
 
-  if (type != 'e' && type != '<' && type != '1' && type != '2' && type != 'r')
+  if (type != 'e' && type != '<' && type != '1' && type != '2' && type != 'r'
+      && type != 'b')
     return 0;
 
   switch (TREE_CODE (exp))
@@ -128,10 +142,23 @@ calls_alloca (exp)
       break;
 
     case BLOCK:
-      /* Must not look at BLOCK_SUPERCONTEXT since it will point back to
-        us.  */
-      length = 3;
-      break;
+      {
+       register tree local;
+
+       for (local = BLOCK_VARS (exp); local; local = TREE_CHAIN (local))
+         if (DECL_INITIAL (local) != 0 && calls_alloca (DECL_INITIAL (local)))
+           return 1;
+      }
+      {
+       register tree subblock;
+
+       for (subblock = BLOCK_SUBBLOCKS (exp);
+            subblock;
+            subblock = TREE_CHAIN (subblock))
+         if (calls_alloca (subblock))
+           return 1;
+      }
+      return 0;
 
     case METHOD_CALL_EXPR:
       length = 3;
@@ -254,8 +281,8 @@ emit_call_1 (funexp, funtype, stack_size, struct_value_size, next_arg_reg,
      rtx use_insns;
      int is_const;
 {
-  rtx stack_size_rtx = gen_rtx (CONST_INT, VOIDmode, stack_size);
-  rtx struct_value_size_rtx = gen_rtx (CONST_INT, VOIDmode, struct_value_size);
+  rtx stack_size_rtx = GEN_INT (stack_size);
+  rtx struct_value_size_rtx = GEN_INT (struct_value_size);
   rtx call_insn;
   int already_popped = 0;
 
@@ -270,8 +297,7 @@ emit_call_1 (funexp, funtype, stack_size, struct_value_size, next_arg_reg,
   if (HAVE_call_pop && HAVE_call_value_pop
       && (RETURN_POPS_ARGS (funtype, stack_size) > 0 || stack_size == 0))
     {
-      rtx n_pop = gen_rtx (CONST_INT, VOIDmode, 
-                          RETURN_POPS_ARGS (funtype, stack_size));
+      rtx n_pop = GEN_INT (RETURN_POPS_ARGS (funtype, stack_size));
       rtx pat;
 
       /* If this subroutine pops its own args, record that in the call insn
@@ -336,7 +362,7 @@ emit_call_1 (funexp, funtype, stack_size, struct_value_size, next_arg_reg,
       if (!already_popped)
        emit_insn (gen_rtx (CLOBBER, VOIDmode, stack_pointer_rtx));
       stack_size -= RETURN_POPS_ARGS (funtype, stack_size);
-      stack_size_rtx = gen_rtx (CONST_INT, VOIDmode, stack_size);
+      stack_size_rtx = GEN_INT (stack_size);
     }
 
   if (stack_size != 0)
@@ -432,7 +458,7 @@ expand_call (exp, target, ignore)
 #endif
 #endif
 
-  /* Size of the stack reserved for paramter registers.  */
+  /* Size of the stack reserved for parameter registers.  */
   int reg_parm_stack_space = 0;
 
   /* 1 if scanning parms front to back, -1 if scanning back to front.  */
@@ -468,6 +494,7 @@ expand_call (exp, target, ignore)
 
   rtx old_stack_level = 0;
   int old_pending_adj;
+  int old_stack_arg_under_construction;
   int old_inhibit_defer_pop = inhibit_defer_pop;
   tree old_cleanups = cleanups_this_call;
 
@@ -503,7 +530,7 @@ expand_call (exp, target, ignore)
 
                 Use abstraction instead of setting TREE_ADDRESSABLE
                 directly.  */
-             if (TREE_INLINE (fndecl) && extra_warnings && !flag_no_inline)
+             if (DECL_INLINE (fndecl) && extra_warnings && !flag_no_inline)
                warning_with_decl (fndecl, "can't inline call to `%s' which was declared inline");
              mark_addressable (fndecl);
            }
@@ -576,14 +603,17 @@ expand_call (exp, target, ignore)
   if (is_integrable)
     {
       rtx temp;
+      rtx before_call = get_last_insn ();
 
       temp = expand_inline_function (fndecl, actparms, target,
                                     ignore, TREE_TYPE (exp),
                                     structure_value_addr);
 
       /* If inlining succeeded, return.  */
-      if ((int) temp != -1)
+      if ((HOST_WIDE_INT) temp != -1)
        {
+         int i;
+
          /* Perform all cleanups needed for the arguments of this call
             (i.e. destructors in C++).  It is ok if these destructors
             clobber RETURN_VALUE_REG, because the only time we care about
@@ -591,6 +621,63 @@ expand_call (exp, target, ignore)
             care to never return that register directly.  */
          expand_cleanups_to (old_cleanups);
 
+#ifdef ACCUMULATE_OUTGOING_ARGS
+         /* If the outgoing argument list must be preserved, push
+            the stack before executing the inlined function if it
+            makes any calls.  */
+
+         for (i = reg_parm_stack_space - 1; i >= 0; i--)
+           if (i < highest_outgoing_arg_in_use && stack_usage_map[i] != 0)
+             break;
+
+         if (stack_arg_under_construction || i >= 0)
+           {
+             rtx insn = NEXT_INSN (before_call), seq;
+
+             /* Look for a call in the inline function code.
+                If OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)) is
+                nonzero then there is a call and it is not necessary
+                to scan the insns.  */
+
+             if (OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl)) == 0)
+               for (; insn; insn = NEXT_INSN (insn))
+                 if (GET_CODE (insn) == CALL_INSN)
+                   break;
+
+             if (insn)
+               {
+                 /* Reserve enough stack space so that the largest
+                    argument list of any function call in the inline
+                    function does not overlap the argument list being
+                    evaluated.  This is usually an overestimate because
+                    allocate_dynamic_stack_space reserves space for an
+                    outgoing argument list in addition to the requested
+                    space, but there is no way to ask for stack space such
+                    that an argument list of a certain length can be
+                    safely constructed.  */
+
+                 int adjust = OUTGOING_ARGS_SIZE (DECL_SAVED_INSNS (fndecl));
+#ifdef REG_PARM_STACK_SPACE
+                 /* Add the stack space reserved for register arguments
+                    in the inline function.  What is really needed is the
+                    largest value of reg_parm_stack_space in the inline
+                    function, but that is not available.  Using the current
+                    value of reg_parm_stack_space is wrong, but gives
+                    correct results on all supported machines.  */
+                 adjust += reg_parm_stack_space;
+#endif
+                 start_sequence ();
+                 emit_stack_save (SAVE_BLOCK, &old_stack_level, 0);
+                 allocate_dynamic_stack_space (GEN_INT (adjust),
+                                               NULL_RTX, BITS_PER_UNIT);
+                 seq = get_insns ();
+                 end_sequence ();
+                 emit_insns_before (seq, NEXT_INSN (before_call));
+                 emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
+               }
+           }
+#endif
+
          /* If the result is equivalent to TARGET, return TARGET to simplify
             checks in store_expr.  They can be equivalent but not equal in the
             case of a function that returns BLKmode.  */
@@ -695,16 +782,26 @@ expand_call (exp, target, ignore)
   push_temp_slots ();
 
   /* Start updating where the next arg would go.  */
-  INIT_CUMULATIVE_ARGS (args_so_far, funtype, 0);
+  INIT_CUMULATIVE_ARGS (args_so_far, funtype, NULL_PTR);
 
   /* If struct_value_rtx is 0, it means pass the address
      as if it were an extra parameter.  */
   if (structure_value_addr && struct_value_rtx == 0)
     {
+#ifdef ACCUMULATE_OUTGOING_ARGS
+      /* If the stack will be adjusted, make sure the structure address
+        does not refer to virtual_outgoing_args_rtx.  */
+      rtx temp = (stack_arg_under_construction
+                 ? copy_addr_to_reg (structure_value_addr)
+                 : force_reg (Pmode, structure_value_addr));
+#else
+      rtx temp = force_reg (Pmode, structure_value_addr);
+#endif
+
       actparms
        = tree_cons (error_mark_node,
                     make_tree (build_pointer_type (TREE_TYPE (funtype)),
-                               force_reg (Pmode, structure_value_addr)),
+                               temp),
                     actparms);
       structure_value_addr_parm = 1;
     }
@@ -760,6 +857,7 @@ expand_call (exp, target, ignore)
   for (p = actparms, argpos = 0; p; p = TREE_CHAIN (p), i += inc, argpos++)
     {
       tree type = TREE_TYPE (TREE_VALUE (p));
+      enum machine_mode mode;
 
       args[i].tree_value = TREE_VALUE (p);
 
@@ -795,18 +893,18 @@ expand_call (exp, target, ignore)
            {
              /* This is a variable-sized object.  Make space on the stack
                 for it.  */
-             rtx size_rtx = expand_expr (size_in_bytes (type), 0,
+             rtx size_rtx = expand_expr (size_in_bytes (type), NULL_RTX,
                                          VOIDmode, 0);
 
              if (old_stack_level == 0)
                {
-                 emit_stack_save (SAVE_BLOCK, &old_stack_level, 0);
+                 emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
                  old_pending_adj = pending_stack_adjust;
                  pending_stack_adjust = 0;
                }
 
              copy = gen_rtx (MEM, BLKmode,
-                             allocate_dynamic_stack_space (size_rtx, 0,
+                             allocate_dynamic_stack_space (size_rtx, NULL_RTX,
                                                            TYPE_ALIGN (type)));
            }
          else
@@ -820,16 +918,31 @@ expand_call (exp, target, ignore)
        }
 #endif
 
-      args[i].reg = FUNCTION_ARG (args_so_far, TYPE_MODE (type), type,
+      mode = TYPE_MODE (type);
+
+#ifdef PROMOTE_FUNCTION_ARGS
+      /* Compute the mode in which the arg is actually to be extended to.  */
+      if (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == ENUMERAL_TYPE
+         || TREE_CODE (type) == BOOLEAN_TYPE || TREE_CODE (type) == CHAR_TYPE
+         || TREE_CODE (type) == REAL_TYPE || TREE_CODE (type) == POINTER_TYPE
+         || TREE_CODE (type) == OFFSET_TYPE)
+       {
+         int unsignedp = TREE_UNSIGNED (type);
+         PROMOTE_MODE (mode, unsignedp, type);
+         args[i].unsignedp = unsignedp;
+       }
+#endif
+
+      args[i].reg = FUNCTION_ARG (args_so_far, mode, type,
                                  argpos < n_named_args);
 #ifdef FUNCTION_ARG_PARTIAL_NREGS
       if (args[i].reg)
        args[i].partial
-         = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, TYPE_MODE (type), type,
+         = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, type,
                                        argpos < n_named_args);
 #endif
 
-      args[i].pass_on_stack = MUST_PASS_IN_STACK (TYPE_MODE (type), type);
+      args[i].pass_on_stack = MUST_PASS_IN_STACK (mode, type);
 
       /* If FUNCTION_ARG returned an (expr_list (nil) FOO), it means that
         we are to pass this arg in the register(s) designated by FOO, but
@@ -1008,7 +1121,8 @@ expand_call (exp, target, ignore)
              += int_size_in_bytes (TREE_TYPE (args[i].tree_value));
        }
 
-      if (copy_to_evaluate_size >= args_size.constant / 2)
+      if (copy_to_evaluate_size * 2 >= args_size.constant
+         && args_size.constant > 0)
        must_preallocate = 1;
     }
 
@@ -1039,7 +1153,7 @@ expand_call (exp, target, ignore)
            && calls_alloca (args[i].tree_value)))
       {
        args[i].initial_value = args[i].value
-         = expand_expr (args[i].tree_value, 0, VOIDmode, 0);
+         = expand_expr (args[i].tree_value, NULL_RTX, VOIDmode, 0);
        preserve_temp_slots (args[i].value);
        free_temp_slots ();
 
@@ -1060,9 +1174,16 @@ expand_call (exp, target, ignore)
     {
       if (old_stack_level == 0)
        {
-         emit_stack_save (SAVE_BLOCK, &old_stack_level, 0);
+         emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
          old_pending_adj = pending_stack_adjust;
          pending_stack_adjust = 0;
+#ifdef ACCUMULATE_OUTGOING_ARGS
+         /* stack_arg_under_construction says whether a stack arg is
+            being constructed at the old stack level.  Pushing the stack
+            gets a clean outgoing argument block.  */
+         old_stack_arg_under_construction = stack_arg_under_construction;
+         stack_arg_under_construction = 0;
+#endif
        }
       argblock = push_block (ARGS_SIZE_RTX (args_size), 0, 0);
     }
@@ -1117,10 +1238,13 @@ expand_call (exp, target, ignore)
        bzero (&stack_usage_map[initial_highest_arg_in_use],
               highest_outgoing_arg_in_use - initial_highest_arg_in_use);
       needed = 0;
-      /* No need to copy this virtual register; the space we're
-        using gets preallocated at the start of the function
-        so the stack pointer won't change here.  */
+
+      /* The address of the outgoing argument list must not be copied to a
+        register here, because argblock would be left pointing to the
+        wrong place after the call to allocate_dynamic_stack_space below. */
+
       argblock = virtual_outgoing_args_rtx;
+
 #else /* not ACCUMULATE_OUTGOING_ARGS */
       if (inhibit_defer_pop == 0)
        {
@@ -1142,7 +1266,7 @@ expand_call (exp, target, ignore)
       if (needed == 0)
        argblock = virtual_outgoing_args_rtx;
       else
-       argblock = push_block (gen_rtx (CONST_INT, VOIDmode, needed), 0, 0);
+       argblock = push_block (GEN_INT (needed), 0, 0);
 
       /* We only really need to call `copy_to_reg' in the case where push
         insns are going to be used to pass ARGBLOCK to a function
@@ -1154,6 +1278,46 @@ expand_call (exp, target, ignore)
 #endif /* not ACCUMULATE_OUTGOING_ARGS */
     }
 
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+  /* The save/restore code in store_one_arg handles all cases except one:
+     a constructor call (including a C function returning a BLKmode struct)
+     to initialize an argument.  */
+  if (stack_arg_under_construction)
+    {
+#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE)
+      rtx push_size = GEN_INT (reg_parm_stack_space + args_size.constant);
+#else
+      rtx push_size = GEN_INT (args_size.constant);
+#endif
+      if (old_stack_level == 0)
+       {
+         emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
+         old_pending_adj = pending_stack_adjust;
+         pending_stack_adjust = 0;
+         /* stack_arg_under_construction says whether a stack arg is
+            being constructed at the old stack level.  Pushing the stack
+            gets a clean outgoing argument block.  */
+         old_stack_arg_under_construction = stack_arg_under_construction;
+         stack_arg_under_construction = 0;
+         /* Make a new map for the new argument list.  */
+         stack_usage_map = (char *)alloca (highest_outgoing_arg_in_use);
+         bzero (stack_usage_map, highest_outgoing_arg_in_use);
+         highest_outgoing_arg_in_use = 0;
+       }
+      allocate_dynamic_stack_space (push_size, NULL_RTX, BITS_PER_UNIT);
+    }
+  /* If argument evaluation might modify the stack pointer, copy the
+     address of the argument list to a register.  */
+  for (i = 0; i < num_actuals; i++)
+    if (args[i].pass_on_stack)
+      {
+       argblock = copy_addr_to_reg (argblock);
+       break;
+      }
+#endif
+
+
   /* If we preallocated stack space, compute the address of each argument.
      We need not ensure it is a valid memory address here; it will be 
      validized when it is used.  */
@@ -1200,9 +1364,8 @@ expand_call (exp, target, ignore)
   /* If we push args individually in reverse order, perform stack alignment
      before the first push (the last arg).  */
   if (argblock == 0)
-    anti_adjust_stack (gen_rtx (CONST_INT, VOIDmode,
-                               (args_size.constant
-                                - original_args_size.constant)));
+    anti_adjust_stack (GEN_INT (args_size.constant
+                               - original_args_size.constant));
 #endif
 #endif
 
@@ -1218,7 +1381,7 @@ expand_call (exp, target, ignore)
   else
     /* Generate an rtx (probably a pseudo-register) for the address.  */
     {
-      funexp = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0);
+      funexp = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0);
       free_temp_slots ();      /* FUNEXP can't be BLKmode */
       emit_queue ();
     }
@@ -1241,11 +1404,14 @@ expand_call (exp, target, ignore)
   for (i = 0; i < num_actuals; i++)
     if (args[i].reg != 0 && ! args[i].pass_on_stack)
       {
+       enum machine_mode mode;
+
        reg_parm_seen = 1;
 
        if (args[i].value == 0)
          {
-           args[i].value = expand_expr (args[i].tree_value, 0, VOIDmode, 0);
+           args[i].value = expand_expr (args[i].tree_value, NULL_RTX,
+                                        VOIDmode, 0);
            preserve_temp_slots (args[i].value);
            free_temp_slots ();
 
@@ -1253,6 +1419,15 @@ expand_call (exp, target, ignore)
               but PCC has one, so this will avoid some problems.  */
            emit_queue ();
          }
+
+       /* If we are to promote the function arg to a wider mode,
+          do it now.  */
+       mode = (GET_CODE (args[i].reg) == EXPR_LIST 
+               ? GET_MODE (XEXP (args[i].reg, 0)) : GET_MODE (args[i].reg));
+
+       if (GET_MODE (args[i].value) != mode)
+         args[i].value = convert_to_mode (mode, args[i].value,
+                                          args[i].unsignedp);
       }
 
 #if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
@@ -1294,7 +1469,7 @@ expand_call (exp, target, ignore)
        {
          save_area = assign_stack_temp (BLKmode, num_to_save, 1);
          emit_block_move (validize_mem (save_area), stack_area,
-                          gen_rtx (CONST_INT, VOIDmode, num_to_save),
+                          GEN_INT (num_to_save),
                           PARM_BOUNDARY / BITS_PER_UNIT);
        }
       else
@@ -1330,18 +1505,26 @@ expand_call (exp, target, ignore)
   /* If we pushed args in forward order, perform stack alignment
      after pushing the last arg.  */
   if (argblock == 0)
-    anti_adjust_stack (gen_rtx (CONST_INT, VOIDmode,
-                               (args_size.constant
-                                - original_args_size.constant)));
+    anti_adjust_stack (GEN_INT (args_size.constant
+                               - original_args_size.constant));
 #endif
 #endif
 
+  /* If register arguments require space on the stack and stack space
+     was not preallocated, allocate stack space here for arguments
+     passed in registers.  */
+#if ! defined(ALLOCATE_OUTGOING_ARGS) && defined(OUTGOING_REG_PARM_STACK_SPACE)
+  if (must_preallocate == 0 && reg_parm_stack_space > 0)
+    anti_adjust_stack (GEN_INT (reg_parm_stack_space));
+#endif
+
   /* Pass the function the address in which to return a structure value.  */
   if (structure_value_addr && ! structure_value_addr_parm)
     {
       emit_move_insn (struct_value_rtx,
                      force_reg (Pmode,
-                                force_operand (structure_value_addr, 0)));
+                                force_operand (structure_value_addr,
+                                               NULL_RTX)));
       if (GET_CODE (struct_value_rtx) == REG)
        {
          push_to_sequence (use_insns);
@@ -1484,15 +1667,27 @@ expand_call (exp, target, ignore)
   else if (structure_value_addr)
     {
       if (target == 0 || GET_CODE (target) != MEM)
-       target = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)),
-                         memory_address (TYPE_MODE (TREE_TYPE (exp)),
-                                         structure_value_addr));
+       {
+         target = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)),
+                           memory_address (TYPE_MODE (TREE_TYPE (exp)),
+                                           structure_value_addr));
+         MEM_IN_STRUCT_P (target)
+           = (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE
+              || TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE
+              || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE);
+       }
     }
   else if (pcc_struct_value)
     {
       if (target == 0)
-       target = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)),
-                         copy_to_reg (valreg));
+       {
+         target = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)),
+                           copy_to_reg (valreg));
+         MEM_IN_STRUCT_P (target)
+           = (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE
+              || TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE
+              || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE);
+       }
       else if (TYPE_MODE (TREE_TYPE (exp)) != BLKmode)
        emit_move_insn (target, gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)),
                                         copy_to_reg (valreg)));
@@ -1501,7 +1696,8 @@ expand_call (exp, target, ignore)
                         expr_size (exp),
                         TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT);
     }
-  else if (target && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp)))
+  else if (target && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp))
+          && GET_MODE (target) == GET_MODE (valreg))
     /* TARGET and VALREG cannot be equal at this point because the latter
        would not have REG_FUNCTION_VALUE_P true, while the former would if
        it were referring to the same register.
@@ -1512,18 +1708,47 @@ expand_call (exp, target, ignore)
   else
     target = copy_to_reg (valreg);
 
+#ifdef PROMOTE_FUNCTION_RETURN
+  /* If we promoted this return value, make the proper SUBREG.  */
+  if (GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp)))
+    {
+      enum machine_mode mode = GET_MODE (target);
+      int unsignedp = TREE_UNSIGNED (TREE_TYPE (exp));
+
+      if (TREE_CODE (TREE_TYPE (exp)) == INTEGER_TYPE
+         || TREE_CODE (TREE_TYPE (exp)) == ENUMERAL_TYPE
+         || TREE_CODE (TREE_TYPE (exp)) == BOOLEAN_TYPE
+         || TREE_CODE (TREE_TYPE (exp)) == CHAR_TYPE
+         || TREE_CODE (TREE_TYPE (exp)) == REAL_TYPE
+         || TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE
+         || TREE_CODE (TREE_TYPE (exp)) == OFFSET_TYPE)
+       {
+         PROMOTE_MODE (mode, unsignedp, TREE_TYPE (exp));
+       }
+
+      target = gen_rtx (SUBREG, TYPE_MODE (TREE_TYPE (exp)), target, 0);
+      SUBREG_PROMOTED_VAR_P (target) = 1;
+      SUBREG_PROMOTED_UNSIGNED_P (target) = unsignedp;
+    }
+#endif
+
   /* Perform all cleanups needed for the arguments of this call
      (i.e. destructors in C++).  */
   expand_cleanups_to (old_cleanups);
 
-  /* If size of args is variable, restore saved stack-pointer value.  */
+  /* If size of args is variable or this was a constructor call for a stack
+     argument, restore saved stack-pointer value.  */
 
   if (old_stack_level)
     {
-      emit_stack_restore (SAVE_BLOCK, old_stack_level, 0);
+      emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
       pending_stack_adjust = old_pending_adj;
+#ifdef ACCUMULATE_OUTGOING_ARGS
+      stack_arg_under_construction = old_stack_arg_under_construction;
+      highest_outgoing_arg_in_use = initial_highest_arg_in_use;
+      stack_usage_map = initial_stack_usage_map;
+#endif
     }
-
 #ifdef ACCUMULATE_OUTGOING_ARGS
   else
     {
@@ -1540,9 +1765,8 @@ expand_call (exp, target, ignore)
            emit_move_insn (stack_area, save_area);
          else
            emit_block_move (stack_area, validize_mem (save_area),
-                            gen_rtx (CONST_INT, VOIDmode,
-                                     high_to_save - low_to_save + 1,
-                                     PARM_BOUNDARY / BITS_PER_UNIT));
+                            GEN_INT (high_to_save - low_to_save + 1),
+                            PARM_BOUNDARY / BITS_PER_UNIT);
        }
 #endif
          
@@ -1560,8 +1784,7 @@ expand_call (exp, target, ignore)
              emit_move_insn (stack_area, args[i].save_area);
            else
              emit_block_move (stack_area, validize_mem (args[i].save_area),
-                              gen_rtx (CONST_INT, VOIDmode,
-                                       args[i].size.constant),
+                              GEN_INT (args[i].size.constant),
                               PARM_BOUNDARY / BITS_PER_UNIT);
          }
 
@@ -1575,7 +1798,7 @@ expand_call (exp, target, ignore)
      for non-local gotos. */
 
   if (may_be_alloca && nonlocal_goto_handler_slot != 0)
-    emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, 0);
+    emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX);
 
   pop_temp_slots ();
 
@@ -1702,8 +1925,7 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl,
              arg->save_area = assign_stack_temp (BLKmode,
                                                  arg->size.constant, 1);
              emit_block_move (validize_mem (arg->save_area), stack_area,
-                              gen_rtx (CONST_INT, VOIDmode,
-                                       arg->size.constant),
+                              GEN_INT (arg->size.constant),
                               PARM_BOUNDARY / BITS_PER_UNIT);
            }
          else
@@ -1734,7 +1956,36 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl,
   /* If this is being passes partially in a register, we can't evaluate
      it directly into its stack slot.  Otherwise, we can.  */
   if (arg->value == 0)
-    arg->value = expand_expr (pval, partial ? 0 : arg->stack, VOIDmode, 0);
+    {
+#ifdef ACCUMULATE_OUTGOING_ARGS
+      /* stack_arg_under_construction is nonzero if a function argument is
+        being evaluated directly into the outgoing argument list and
+        expand_call must take special action to preserve the argument list
+        if it is called recursively.
+
+        For scalar function arguments stack_usage_map is sufficient to
+        determine which stack slots must be saved and restored.  Scalar
+        arguments in general have pass_on_stack == 0.
+
+        If this argument is initialized by a function which takes the
+        address of the argument (a C++ constructor or a C function
+        returning a BLKmode structure), then stack_usage_map is
+        insufficient and expand_call must push the stack around the
+        function call.  Such arguments have pass_on_stack == 1.
+
+        Note that it is always safe to set stack_arg_under_construction,
+        but this generates suboptimal code if set when not needed.  */
+
+      if (arg->pass_on_stack)
+       stack_arg_under_construction++;
+#endif
+      arg->value = expand_expr (pval, partial ? NULL_RTX : arg->stack,
+                               VOIDmode, 0);
+#ifdef ACCUMULATE_OUTGOING_ARGS
+      if (arg->pass_on_stack)
+       stack_arg_under_construction--;
+#endif
+    }
 
   /* Don't allow anything left on stack from computation
      of argument to alloca.  */
@@ -1807,7 +2058,7 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl,
             emit_push_insn for BLKmode is careful to avoid it.  */
          excess = (arg->size.constant - TREE_INT_CST_LOW (size)
                    + partial * UNITS_PER_WORD);
-         size_rtx = expand_expr (size, 0, VOIDmode, 0);
+         size_rtx = expand_expr (size, NULL_RTX, VOIDmode, 0);
        }
 
       emit_push_insn (arg->value, TYPE_MODE (TREE_TYPE (pval)),