OSDN Git Service

2009-10-19 Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
[pf3gnuchains/gcc-fork.git] / gcc / calls.c
index 03994d4..13167a6 100644 (file)
@@ -1,6 +1,6 @@
 /* Convert function calls to rtl insns, for GNU C compiler.
    Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
    Free Software Foundation, Inc.
 
 This file is part of GCC.
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "sbitmap.h"
 #include "langhooks.h"
 #include "target.h"
+#include "debug.h"
 #include "cgraph.h"
 #include "except.h"
 #include "dbgcnt.h"
@@ -166,7 +167,7 @@ static void restore_fixed_argument_area (rtx, rtx, int, int);
    CALL_INSN_FUNCTION_USAGE information.  */
 
 rtx
-prepare_call_address (rtx funexp, rtx static_chain_value,
+prepare_call_address (tree fndecl, rtx funexp, rtx static_chain_value,
                      rtx *call_fusage, int reg_parm_seen, int sibcallp)
 {
   /* Make a valid memory address and copy constants through pseudo-regs,
@@ -187,11 +188,15 @@ prepare_call_address (rtx funexp, rtx static_chain_value,
 
   if (static_chain_value != 0)
     {
+      rtx chain;
+
+      gcc_assert (fndecl);
+      chain = targetm.calls.static_chain (fndecl, false);
       static_chain_value = convert_memory_address (Pmode, static_chain_value);
-      emit_move_insn (static_chain_rtx, static_chain_value);
 
-      if (REG_P (static_chain_rtx))
-       use_reg (call_fusage, static_chain_rtx);
+      emit_move_insn (chain, static_chain_value);
+      if (REG_P (chain))
+       use_reg (call_fusage, chain);
     }
 
   return funexp;
@@ -238,7 +243,7 @@ prepare_call_address (rtx funexp, rtx static_chain_value,
    denote registers used by the called function.  */
 
 static void
-emit_call_1 (rtx funexp, tree fntree, tree fndecl ATTRIBUTE_UNUSED,
+emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNUSED,
             tree funtype ATTRIBUTE_UNUSED,
             HOST_WIDE_INT stack_size ATTRIBUTE_UNUSED,
             HOST_WIDE_INT rounded_stack_size,
@@ -376,19 +381,8 @@ emit_call_1 (rtx funexp, tree fntree, tree fndecl ATTRIBUTE_UNUSED,
   if (ecf_flags & ECF_LOOPING_CONST_OR_PURE)
     RTL_LOOPING_CONST_OR_PURE_CALL_P (call_insn) = 1;
 
-  /* If this call can't throw, attach a REG_EH_REGION reg note to that
-     effect.  */
-  if (ecf_flags & ECF_NOTHROW)
-    add_reg_note (call_insn, REG_EH_REGION, const0_rtx);
-  else
-    {
-      int rn = lookup_expr_eh_region (fntree);
-
-      /* If rn < 0, then either (1) tree-ssa not used or (2) doesn't
-        throw, which we already took care of.  */
-      if (rn > 0)
-       add_reg_note (call_insn, REG_EH_REGION, GEN_INT (rn));
-    }
+  /* Create a nothrow REG_EH_REGION note, if needed.  */
+  make_reg_eh_region_note (call_insn, ecf_flags, 0);
 
   if (ecf_flags & ECF_NORETURN)
     add_reg_note (call_insn, REG_NORETURN, const0_rtx);
@@ -401,6 +395,11 @@ emit_call_1 (rtx funexp, tree fntree, tree fndecl ATTRIBUTE_UNUSED,
 
   SIBLING_CALL_P (call_insn) = ((ecf_flags & ECF_SIBCALL) != 0);
 
+  /* Record debug information for virtual calls.  */
+  if (flag_enable_icf_debug && fndecl == NULL)
+    (*debug_hooks->virtual_call_token) (CALL_EXPR_FN (fntree),
+                                        INSN_UID (call_insn));
+
   /* Restore this now, so that we do defer pops for this call's args
      if the context of the call as a whole permits.  */
   inhibit_defer_pop = old_inhibit_defer_pop;
@@ -498,10 +497,14 @@ special_function_p (const_tree fndecl, int flags)
               && ! strcmp (name, "__builtin_alloca"))))
        flags |= ECF_MAY_BE_ALLOCA;
 
-      /* Disregard prefix _, __ or __x.  */
+      /* Disregard prefix _, __, __x or __builtin_.  */
       if (name[0] == '_')
        {
-         if (name[1] == '_' && name[2] == 'x')
+         if (name[1] == '_'
+             && name[2] == 'b'
+             && !strncmp (name + 3, "uiltin_", 7))
+           tname += 10;
+         else if (name[1] == '_' && name[2] == 'x')
            tname += 3;
          else if (name[1] == '_')
            tname += 2;
@@ -945,6 +948,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
                                 int *must_preallocate, int *ecf_flags,
                                 bool *may_tailcall, bool call_from_thunk_p)
 {
+  location_t loc = EXPR_LOCATION (exp);
   /* 1 if scanning parms front to back, -1 if scanning back to front.  */
   int inc;
 
@@ -992,7 +996,6 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
            && targetm.calls.split_complex_arg (argtype))
          {
            tree subtype = TREE_TYPE (argtype);
-           arg = save_expr (arg);
            args[j].tree_value = build1 (REALPART_EXPR, subtype, arg);
            j += inc;
            args[j].tree_value = build1 (IMAGPART_EXPR, subtype, arg);
@@ -1051,6 +1054,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
              || (callee_copies
                  && !TREE_ADDRESSABLE (type)
                  && (base = get_base_address (args[i].tree_value))
+                 && TREE_CODE (base) != SSA_NAME
                  && (!DECL_P (base) || MEM_P (DECL_RTL (base)))))
            {
              /* We can't use sibcalls if a callee-copied argument is
@@ -1058,7 +1062,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
              if (!call_from_thunk_p && DECL_P (base) && !TREE_STATIC (base))
                *may_tailcall = false;
 
-             args[i].tree_value = build_fold_addr_expr (args[i].tree_value);
+             args[i].tree_value = build_fold_addr_expr_loc (loc,
+                                                        args[i].tree_value);
              type = TREE_TYPE (args[i].tree_value);
 
              if (*ecf_flags & ECF_CONST)
@@ -1110,19 +1115,15 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
                *ecf_flags &= ~(ECF_PURE | ECF_LOOPING_CONST_OR_PURE);
 
              args[i].tree_value
-               = build_fold_addr_expr (make_tree (type, copy));
+               = build_fold_addr_expr_loc (loc, make_tree (type, copy));
              type = TREE_TYPE (args[i].tree_value);
              *may_tailcall = false;
            }
        }
 
-      mode = TYPE_MODE (type);
       unsignedp = TYPE_UNSIGNED (type);
-
-      if (targetm.calls.promote_function_args (fndecl
-                                              ? TREE_TYPE (fndecl)
-                                              : fntype))
-       mode = promote_mode (type, mode, &unsignedp, 1);
+      mode = promote_function_mode (type, TYPE_MODE (type), &unsignedp,
+                                   fndecl ? TREE_TYPE (fndecl) : fntype, 0);
 
       args[i].unsignedp = unsignedp;
       args[i].mode = mode;
@@ -1302,29 +1303,33 @@ precompute_arguments (int num_actuals, struct arg_data *args)
 
   for (i = 0; i < num_actuals; i++)
     {
+      tree type;
       enum machine_mode mode;
 
       if (TREE_CODE (args[i].tree_value) != CALL_EXPR)
        continue;
 
       /* If this is an addressable type, we cannot pre-evaluate it.  */
-      gcc_assert (!TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)));
+      type = TREE_TYPE (args[i].tree_value);
+      gcc_assert (!TREE_ADDRESSABLE (type));
 
       args[i].initial_value = args[i].value
        = expand_normal (args[i].tree_value);
 
-      mode = TYPE_MODE (TREE_TYPE (args[i].tree_value));
+      mode = TYPE_MODE (type);
       if (mode != args[i].mode)
        {
+         int unsignedp = args[i].unsignedp;
          args[i].value
            = convert_modes (args[i].mode, mode,
                             args[i].value, args[i].unsignedp);
-#if defined(PROMOTE_FUNCTION_MODE) && !defined(PROMOTE_MODE)
+
          /* CSE will replace this only if it contains args[i].value
             pseudo, so convert it down to the declared mode using
             a SUBREG.  */
          if (REG_P (args[i].value)
-             && GET_MODE_CLASS (args[i].mode) == MODE_INT)
+             && GET_MODE_CLASS (args[i].mode) == MODE_INT
+             && promote_mode (type, mode, &unsignedp) != args[i].mode)
            {
              args[i].initial_value
                = gen_lowpart_SUBREG (mode, args[i].value);
@@ -1332,7 +1337,6 @@ precompute_arguments (int num_actuals, struct arg_data *args)
              SUBREG_PROMOTED_UNSIGNED_SET (args[i].initial_value,
                                            args[i].unsignedp);
            }
-#endif
        }
     }
 }
@@ -1427,7 +1431,7 @@ compute_argument_addresses (struct arg_data *args, rtx argblock, int num_actuals
              && args[i].partial == 0)
            continue;
 
-         if (GET_CODE (offset) == CONST_INT)
+         if (CONST_INT_P (offset))
            addr = plus_constant (arg_reg, INTVAL (offset));
          else
            addr = gen_rtx_PLUS (Pmode, arg_reg, offset);
@@ -1454,14 +1458,14 @@ compute_argument_addresses (struct arg_data *args, rtx argblock, int num_actuals
          boundary = args[i].locate.boundary;
          if (args[i].locate.where_pad != downward)
            align = boundary;
-         else if (GET_CODE (offset) == CONST_INT)
+         else if (CONST_INT_P (offset))
            {
              align = INTVAL (offset) * BITS_PER_UNIT | boundary;
              align = align & -align;
            }
          set_mem_align (args[i].stack, align);
 
-         if (GET_CODE (slot_offset) == CONST_INT)
+         if (CONST_INT_P (slot_offset))
            addr = plus_constant (arg_reg, INTVAL (slot_offset));
          else
            addr = gen_rtx_PLUS (Pmode, arg_reg, slot_offset);
@@ -1545,7 +1549,7 @@ mem_overlaps_already_clobbered_arg_p (rtx addr, unsigned HOST_WIDE_INT size)
     i = 0;
   else if (GET_CODE (addr) == PLUS
           && XEXP (addr, 0) == crtl->args.internal_arg_pointer
-          && GET_CODE (XEXP (addr, 1)) == CONST_INT)
+          && CONST_INT_P (XEXP (addr, 1)))
     i = INTVAL (XEXP (addr, 1));
   /* Return true for arg pointer based indexed addressing.  */
   else if (GET_CODE (addr) == PLUS
@@ -1922,6 +1926,7 @@ expand_call (tree exp, rtx target, int ignore)
   /* Data type of the function.  */
   tree funtype;
   tree type_arg_types;
+  tree rettype;
   /* Declaration of the function being called,
      or 0 if the function is computed (not known by name).  */
   tree fndecl = 0;
@@ -2017,7 +2022,6 @@ expand_call (tree exp, rtx target, int ignore)
   int old_stack_pointer_delta = 0;
 
   rtx call_fusage;
-  tree p = CALL_EXPR_FN (exp);
   tree addr = CALL_EXPR_FN (exp);
   int i;
   /* The alignment of the stack, in bits.  */
@@ -2040,15 +2044,16 @@ expand_call (tree exp, rtx target, int ignore)
     }
   else
     {
-      fntype = TREE_TYPE (TREE_TYPE (p));
+      fntype = TREE_TYPE (TREE_TYPE (addr));
       flags |= flags_from_decl_or_type (fntype);
     }
+  rettype = TREE_TYPE (exp);
 
   struct_value = targetm.calls.struct_value_rtx (fntype, 0);
 
   /* Warn if this value is an aggregate type,
      regardless of which calling convention we are using for it.  */
-  if (AGGREGATE_TYPE_P (TREE_TYPE (exp)))
+  if (AGGREGATE_TYPE_P (rettype))
     warning (OPT_Waggregate_return, "function call has aggregate value");
 
   /* If the result of a non looping pure or const function call is
@@ -2058,7 +2063,7 @@ expand_call (tree exp, rtx target, int ignore)
   if ((flags & (ECF_CONST | ECF_PURE))
       && (!(flags & ECF_LOOPING_CONST_OR_PURE))
       && (ignore || target == const0_rtx
-         || TYPE_MODE (TREE_TYPE (exp)) == VOIDmode))
+         || TYPE_MODE (rettype) == VOIDmode))
     {
       bool volatilep = false;
       tree arg;
@@ -2101,7 +2106,7 @@ expand_call (tree exp, rtx target, int ignore)
       }
 #else /* not PCC_STATIC_STRUCT_RETURN */
       {
-       struct_value_size = int_size_in_bytes (TREE_TYPE (exp));
+       struct_value_size = int_size_in_bytes (rettype);
 
        if (target && MEM_P (target) && CALL_EXPR_RETURN_SLOT_OPT (exp))
          structure_value_addr = XEXP (target, 0);
@@ -2110,7 +2115,7 @@ expand_call (tree exp, rtx target, int ignore)
            /* For variable-sized objects, we must be called with a target
               specified.  If we were to allocate space on the stack here,
               we would have no way of knowing when to free it.  */
-           rtx d = assign_temp (TREE_TYPE (exp), 0, 1, 1);
+           rtx d = assign_temp (rettype, 0, 1, 1);
 
            mark_temp_addr_taken (d);
            structure_value_addr = XEXP (d, 0);
@@ -2281,7 +2286,6 @@ expand_call (tree exp, rtx target, int ignore)
   if (currently_expanding_call++ != 0
       || !flag_optimize_sibling_calls
       || args_size.var
-      || lookup_expr_eh_region (exp) >= 0
       || dbg_cnt (tail_call) == false)
     try_tail_call = 0;
 
@@ -2298,6 +2302,12 @@ expand_call (tree exp, rtx target, int ignore)
         It does not seem worth the effort since few optimizable
         sibling calls will return a structure.  */
       || structure_value_addr != NULL_RTX
+#ifdef REG_PARM_STACK_SPACE
+      /* If outgoing reg parm stack space changes, we can not do sibcall.  */
+      || (OUTGOING_REG_PARM_STACK_SPACE (funtype)
+         != OUTGOING_REG_PARM_STACK_SPACE (TREE_TYPE (current_function_decl)))
+      || (reg_parm_stack_space != REG_PARM_STACK_SPACE (fndecl))
+#endif
       /* Check whether the target is able to optimize the call
         into a sibcall.  */
       || !targetm.function_ok_for_sibcall (fndecl, exp)
@@ -2324,6 +2334,37 @@ expand_call (tree exp, rtx target, int ignore)
       || !lang_hooks.decls.ok_for_sibcall (fndecl))
     try_tail_call = 0;
 
+  /* Check if caller and callee disagree in promotion of function
+     return value.  */
+  if (try_tail_call)
+    {
+      enum machine_mode caller_mode, caller_promoted_mode;
+      enum machine_mode callee_mode, callee_promoted_mode;
+      int caller_unsignedp, callee_unsignedp;
+      tree caller_res = DECL_RESULT (current_function_decl);
+
+      caller_unsignedp = TYPE_UNSIGNED (TREE_TYPE (caller_res));
+      caller_mode = DECL_MODE (caller_res);
+      callee_unsignedp = TYPE_UNSIGNED (TREE_TYPE (funtype));
+      callee_mode = TYPE_MODE (TREE_TYPE (funtype));
+      caller_promoted_mode
+       = promote_function_mode (TREE_TYPE (caller_res), caller_mode,
+                                &caller_unsignedp,
+                                TREE_TYPE (current_function_decl), 1);
+      callee_promoted_mode
+       = promote_function_mode (TREE_TYPE (funtype), callee_mode,
+                                &callee_unsignedp,
+                                funtype, 1);
+      if (caller_mode != VOIDmode
+         && (caller_promoted_mode != callee_promoted_mode
+             || ((caller_mode != caller_promoted_mode
+                  || callee_mode != callee_promoted_mode)
+                 && (caller_unsignedp != callee_unsignedp
+                     || GET_MODE_BITSIZE (caller_mode)
+                        < GET_MODE_BITSIZE (callee_mode)))))
+       try_tail_call = 0;
+    }
+
   /* Ensure current function's preferred stack boundary is at least
      what we need.  Stack alignment may also increase preferred stack
      boundary.  */
@@ -2651,14 +2692,14 @@ expand_call (tree exp, rtx target, int ignore)
 
       /* Figure out the register where the value, if any, will come back.  */
       valreg = 0;
-      if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode
+      if (TYPE_MODE (rettype) != VOIDmode
          && ! structure_value_addr)
        {
          if (pcc_struct_value)
-           valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)),
+           valreg = hard_function_value (build_pointer_type (rettype),
                                          fndecl, NULL, (pass == 0));
          else
-           valreg = hard_function_value (TREE_TYPE (exp), fndecl, fntype,
+           valreg = hard_function_value (rettype, fndecl, fntype,
                                          (pass == 0));
 
          /* If VALREG is a PARALLEL whose first member has a zero
@@ -2699,26 +2740,28 @@ expand_call (tree exp, rtx target, int ignore)
         but we do preallocate space here if they want that.  */
 
       for (i = 0; i < num_actuals; i++)
-       if (args[i].reg == 0 || args[i].pass_on_stack)
-         {
-           rtx before_arg = get_last_insn ();
-
-           if (store_one_arg (&args[i], argblock, flags,
-                              adjusted_args_size.var != 0,
-                              reg_parm_stack_space)
-               || (pass == 0
-                   && check_sibcall_argument_overlap (before_arg,
-                                                      &args[i], 1)))
-             sibcall_failure = 1;
-
-           if (flags & ECF_CONST
-               && args[i].stack
-               && args[i].value == args[i].stack)
-             call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
-                                              gen_rtx_USE (VOIDmode,
-                                                           args[i].value),
-                                              call_fusage);
-         }
+       {
+         if (args[i].reg == 0 || args[i].pass_on_stack)
+           {
+             rtx before_arg = get_last_insn ();
+
+             if (store_one_arg (&args[i], argblock, flags,
+                                adjusted_args_size.var != 0,
+                                reg_parm_stack_space)
+                 || (pass == 0
+                     && check_sibcall_argument_overlap (before_arg,
+                                                        &args[i], 1)))
+               sibcall_failure = 1;
+             }
+
+         if (((flags & ECF_CONST)
+              || ((flags & ECF_PURE) && ACCUMULATE_OUTGOING_ARGS))
+             && args[i].stack)
+           call_fusage = gen_rtx_EXPR_LIST (VOIDmode,
+                                            gen_rtx_USE (VOIDmode,
+                                                         args[i].stack),
+                                            call_fusage);
+       }
 
       /* If we have a parm that is passed in registers but not in memory
         and whose alignment does not permit a direct copy into registers,
@@ -2774,7 +2817,7 @@ expand_call (tree exp, rtx target, int ignore)
        }
 
       after_args = get_last_insn ();
-      funexp = prepare_call_address (funexp, static_chain_value,
+      funexp = prepare_call_address (fndecl, funexp, static_chain_value,
                                     &call_fusage, reg_parm_seen, pass == 0);
 
       load_register_parameters (args, num_actuals, &call_fusage, flags,
@@ -2821,12 +2864,12 @@ expand_call (tree exp, rtx target, int ignore)
         group load/store machinery below.  */
       if (!structure_value_addr
          && !pcc_struct_value
-         && TYPE_MODE (TREE_TYPE (exp)) != BLKmode
-         && targetm.calls.return_in_msb (TREE_TYPE (exp)))
+         && TYPE_MODE (rettype) != BLKmode
+         && targetm.calls.return_in_msb (rettype))
        {
-         if (shift_return_value (TYPE_MODE (TREE_TYPE (exp)), false, valreg))
+         if (shift_return_value (TYPE_MODE (rettype), false, valreg))
            sibcall_failure = 1;
-         valreg = gen_rtx_REG (TYPE_MODE (TREE_TYPE (exp)), REGNO (valreg));
+         valreg = gen_rtx_REG (TYPE_MODE (rettype), REGNO (valreg));
        }
 
       if (pass && (flags & ECF_MALLOC))
@@ -2835,7 +2878,7 @@ expand_call (tree exp, rtx target, int ignore)
          rtx last, insns;
 
          /* The return value from a malloc-like function is a pointer.  */
-         if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
+         if (TREE_CODE (rettype) == POINTER_TYPE)
            mark_reg_pointer (temp, BIGGEST_ALIGNMENT);
 
          emit_move_insn (temp, valreg);
@@ -2885,7 +2928,7 @@ expand_call (tree exp, rtx target, int ignore)
 
       /* If value type not void, return an rtx for the value.  */
 
-      if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode
+      if (TYPE_MODE (rettype) == VOIDmode
          || ignore)
        target = const0_rtx;
       else if (structure_value_addr)
@@ -2893,10 +2936,10 @@ expand_call (tree exp, rtx target, int ignore)
          if (target == 0 || !MEM_P (target))
            {
              target
-               = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
-                              memory_address (TYPE_MODE (TREE_TYPE (exp)),
+               = gen_rtx_MEM (TYPE_MODE (rettype),
+                              memory_address (TYPE_MODE (rettype),
                                               structure_value_addr));
-             set_mem_attributes (target, exp, 1);
+             set_mem_attributes (target, rettype, 1);
            }
        }
       else if (pcc_struct_value)
@@ -2904,9 +2947,9 @@ expand_call (tree exp, rtx target, int ignore)
          /* This is the special C++ case where we need to
             know what the true target was.  We take care to
             never use this value more than once in one expression.  */
-         target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
+         target = gen_rtx_MEM (TYPE_MODE (rettype),
                                copy_to_reg (valreg));
-         set_mem_attributes (target, exp, 1);
+         set_mem_attributes (target, rettype, 1);
        }
       /* Handle calls that return values in multiple non-contiguous locations.
         The Irix 6 ABI has examples of this.  */
@@ -2915,22 +2958,22 @@ expand_call (tree exp, rtx target, int ignore)
          if (target == 0)
            {
              /* This will only be assigned once, so it can be readonly.  */
-             tree nt = build_qualified_type (TREE_TYPE (exp),
-                                             (TYPE_QUALS (TREE_TYPE (exp))
+             tree nt = build_qualified_type (rettype,
+                                             (TYPE_QUALS (rettype)
                                               | TYPE_QUAL_CONST));
 
              target = assign_temp (nt, 0, 1, 1);
            }
 
          if (! rtx_equal_p (target, valreg))
-           emit_group_store (target, valreg, TREE_TYPE (exp),
-                             int_size_in_bytes (TREE_TYPE (exp)));
+           emit_group_store (target, valreg, rettype,
+                             int_size_in_bytes (rettype));
 
          /* We can not support sibling calls for this case.  */
          sibcall_failure = 1;
        }
       else if (target
-              && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp))
+              && GET_MODE (target) == TYPE_MODE (rettype)
               && GET_MODE (target) == GET_MODE (valreg))
        {
          bool may_overlap = false;
@@ -2975,9 +3018,12 @@ expand_call (tree exp, rtx target, int ignore)
                sibcall_failure = 1;
            }
        }
-      else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
+      else if (TYPE_MODE (rettype) == BLKmode)
        {
-         target = copy_blkmode_from_reg (target, valreg, TREE_TYPE (exp));
+         rtx val = valreg;
+         if (GET_MODE (val) != BLKmode)
+           val = avoid_likely_spilled_reg (val);
+         target = copy_blkmode_from_reg (target, val, rettype);
 
          /* We can not support sibling calls for this case.  */
          sibcall_failure = 1;
@@ -2985,38 +3031,37 @@ expand_call (tree exp, rtx target, int ignore)
       else
        target = copy_to_reg (avoid_likely_spilled_reg (valreg));
 
-      if (targetm.calls.promote_function_return(funtype))
+      /* If we promoted this return value, make the proper SUBREG.
+         TARGET might be const0_rtx here, so be careful.  */
+      if (REG_P (target)
+         && TYPE_MODE (rettype) != BLKmode
+         && GET_MODE (target) != TYPE_MODE (rettype))
        {
-         /* If we promoted this return value, make the proper SUBREG.
-            TARGET might be const0_rtx here, so be careful.  */
-         if (REG_P (target)
-             && TYPE_MODE (TREE_TYPE (exp)) != BLKmode
-             && GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp)))
+         tree type = rettype;
+         int unsignedp = TYPE_UNSIGNED (type);
+         int offset = 0;
+         enum machine_mode pmode;
+
+         /* Ensure we promote as expected, and get the new unsignedness.  */
+         pmode = promote_function_mode (type, TYPE_MODE (type), &unsignedp,
+                                        funtype, 1);
+         gcc_assert (GET_MODE (target) == pmode);
+
+         if ((WORDS_BIG_ENDIAN || BYTES_BIG_ENDIAN)
+             && (GET_MODE_SIZE (GET_MODE (target))
+                 > GET_MODE_SIZE (TYPE_MODE (type))))
            {
-             tree type = TREE_TYPE (exp);
-             int unsignedp = TYPE_UNSIGNED (type);
-             int offset = 0;
-             enum machine_mode pmode;
-
-             pmode = promote_mode (type, TYPE_MODE (type), &unsignedp, 1);
-             /* If we don't promote as expected, something is wrong.  */
-             gcc_assert (GET_MODE (target) == pmode);
-
-             if ((WORDS_BIG_ENDIAN || BYTES_BIG_ENDIAN)
-                 && (GET_MODE_SIZE (GET_MODE (target))
-                     > GET_MODE_SIZE (TYPE_MODE (type))))
-               {
-                 offset = GET_MODE_SIZE (GET_MODE (target))
-                   - GET_MODE_SIZE (TYPE_MODE (type));
-                 if (! BYTES_BIG_ENDIAN)
-                   offset = (offset / UNITS_PER_WORD) * UNITS_PER_WORD;
-                 else if (! WORDS_BIG_ENDIAN)
-                   offset %= UNITS_PER_WORD;
-               }
-             target = gen_rtx_SUBREG (TYPE_MODE (type), target, offset);
-             SUBREG_PROMOTED_VAR_P (target) = 1;
-             SUBREG_PROMOTED_UNSIGNED_SET (target, unsignedp);
+             offset = GET_MODE_SIZE (GET_MODE (target))
+               - GET_MODE_SIZE (TYPE_MODE (type));
+             if (! BYTES_BIG_ENDIAN)
+               offset = (offset / UNITS_PER_WORD) * UNITS_PER_WORD;
+             else if (! WORDS_BIG_ENDIAN)
+               offset %= UNITS_PER_WORD;
            }
+
+         target = gen_rtx_SUBREG (TYPE_MODE (type), target, offset);
+         SUBREG_PROMOTED_VAR_P (target) = 1;
+         SUBREG_PROMOTED_UNSIGNED_SET (target, unsignedp);
        }
 
       /* If size of args is variable or this was a constructor call for a stack
@@ -3402,7 +3447,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
   for (; count < nargs; count++)
     {
       rtx val = va_arg (p, rtx);
-      enum machine_mode mode = va_arg (p, enum machine_mode);
+      enum machine_mode mode = (enum machine_mode) va_arg (p, int);
 
       /* We cannot convert the arg value to the mode the library wants here;
         must do it earlier where we know the signedness of the arg.  */
@@ -3592,6 +3637,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       rtx val = argvec[argnum].value;
       rtx reg = argvec[argnum].reg;
       int partial = argvec[argnum].partial;
+      unsigned int parm_align = argvec[argnum].locate.boundary;
       int lower_bound = 0, upper_bound = 0, i;
 
       if (! (reg != 0 && partial == 0))
@@ -3604,10 +3650,10 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 #ifdef ARGS_GROW_DOWNWARD
              /* stack_slot is negative, but we want to index stack_usage_map
                 with positive values.  */
-             upper_bound = -argvec[argnum].locate.offset.constant + 1;
+             upper_bound = -argvec[argnum].locate.slot_offset.constant + 1;
              lower_bound = upper_bound - argvec[argnum].locate.size.constant;
 #else
-             lower_bound = argvec[argnum].locate.offset.constant;
+             lower_bound = argvec[argnum].locate.slot_offset.constant;
              upper_bound = lower_bound + argvec[argnum].locate.size.constant;
 #endif
 
@@ -3653,7 +3699,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
                }
            }
 
-         emit_push_insn (val, mode, NULL_TREE, NULL_RTX, PARM_BOUNDARY,
+         emit_push_insn (val, mode, NULL_TREE, NULL_RTX, parm_align,
                          partial, reg, 0, argblock,
                          GEN_INT (argvec[argnum].locate.offset.constant),
                          reg_parm_stack_space,
@@ -3666,7 +3712,8 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 
          NO_DEFER_POP;
 
-         if (flags & ECF_CONST)
+         if ((flags & ECF_CONST)
+             || ((flags & ECF_PURE) && ACCUMULATE_OUTGOING_ARGS))
            {
              rtx use;
 
@@ -3701,7 +3748,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
   else
     argnum = 0;
 
-  fun = prepare_call_address (fun, NULL, &call_fusage, 0, 0);
+  fun = prepare_call_address (NULL, fun, NULL, &call_fusage, 0, 0);
 
   /* Now load any reg parms into their regs.  */
 
@@ -3760,7 +3807,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
      cse'ing of library calls could delete a call and leave the pop.  */
   NO_DEFER_POP;
   valreg = (mem_value == 0 && outmode != VOIDmode
-           ? hard_libcall_value (outmode) : NULL_RTX);
+           ? hard_libcall_value (outmode, orgfun) : NULL_RTX);
 
   /* Stack must be properly aligned now.  */
   gcc_assert (!(stack_pointer_delta
@@ -3829,15 +3876,14 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
        }
       else
        {
-         /* Convert to the proper mode if PROMOTE_MODE has been active.  */
+         /* Convert to the proper mode if a promotion has been active.  */
          if (GET_MODE (valreg) != outmode)
            {
              int unsignedp = TYPE_UNSIGNED (tfom);
 
-             gcc_assert (targetm.calls.promote_function_return (tfom));
-             gcc_assert (promote_mode (tfom, outmode, &unsignedp, 0)
+             gcc_assert (promote_function_mode (tfom, outmode, &unsignedp,
+                                                fndecl ? TREE_TYPE (fndecl) : fntype, 1)
                          == GET_MODE (valreg));
-
              valreg = convert_modes (outmode, GET_MODE (valreg), valreg, 0);
            }
 
@@ -4189,7 +4235,8 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
                    - int_size_in_bytes (TREE_TYPE (pval))
                    + partial);
          size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
-                                 NULL_RTX, TYPE_MODE (sizetype), 0);
+                                 NULL_RTX, TYPE_MODE (sizetype),
+                                 EXPAND_NORMAL);
        }
 
       parm_align = arg->locate.boundary;
@@ -4218,7 +4265,7 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
              || (GET_CODE (XEXP (x, 0)) == PLUS
                  && XEXP (XEXP (x, 0), 0) ==
                     crtl->args.internal_arg_pointer
-                 && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT))
+                 && CONST_INT_P (XEXP (XEXP (x, 0), 1))))
            {
              if (XEXP (x, 0) != crtl->args.internal_arg_pointer)
                i = INTVAL (XEXP (XEXP (x, 0), 1));
@@ -4226,7 +4273,7 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
              /* expand_call should ensure this.  */
              gcc_assert (!arg->locate.offset.var
                          && arg->locate.size.var == 0
-                         && GET_CODE (size_rtx) == CONST_INT);
+                         && CONST_INT_P (size_rtx));
 
              if (arg->locate.offset.constant > i)
                {