]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/function.c
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / function.c
index 954e9468f01f6d325bd34167f023eae374de5934..af3d57b32a36785bd679ef6e35e57040c5f847ee 100644 (file)
@@ -1,5 +1,5 @@
 /* Expands front end tree to back end RTL for GCC.
-   Copyright (C) 1987-2018 Free Software Foundation, Inc.
+   Copyright (C) 1987-2021 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -46,10 +46,12 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "expmed.h"
 #include "optabs.h"
+#include "opts.h"
 #include "regs.h"
 #include "emit-rtl.h"
 #include "recog.h"
 #include "rtl-error.h"
+#include "hard-reg-set.h"
 #include "alias.h"
 #include "fold-const.h"
 #include "stor-layout.h"
@@ -79,6 +81,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "gimple.h"
 #include "options.h"
+#include "function-abi.h"
+#include "value-range.h"
+#include "gimple-range.h"
 
 /* So we can assign to cfun in this file.  */
 #undef cfun
@@ -133,7 +138,7 @@ vec<tree, va_gc> *types_used_by_cur_var_decl;
 
 /* Forward declarations.  */
 
-static struct temp_slot *find_temp_slot_from_address (rtx);
+static class temp_slot *find_temp_slot_from_address (rtx);
 static void pad_to_arg_alignment (struct args_size *, int, struct args_size *);
 static void pad_below (struct args_size *, machine_mode, tree);
 static void reorder_blocks_1 (rtx_insn *, tree, vec<tree> *);
@@ -345,7 +350,7 @@ try_fit_stack_local (poly_int64 start, poly_int64 length,
 static void
 add_frame_space (poly_int64 start, poly_int64 end)
 {
-  struct frame_space *space = ggc_alloc<frame_space> ();
+  class frame_space *space = ggc_alloc<frame_space> ();
   space->next = crtl->frame_space_list;
   crtl->frame_space_list = space;
   space->start = start;
@@ -377,7 +382,6 @@ assign_stack_local_1 (machine_mode mode, poly_int64 size,
   poly_int64 bigend_correction = 0;
   poly_int64 slot_offset = 0, old_frame_offset;
   unsigned int alignment, alignment_in_bits;
-  bool dynamic_align_addr = false;
 
   if (align == 0)
     {
@@ -396,20 +400,14 @@ assign_stack_local_1 (machine_mode mode, poly_int64 size,
 
   alignment_in_bits = alignment * BITS_PER_UNIT;
 
+  /* Ignore alignment if it exceeds MAX_SUPPORTED_STACK_ALIGNMENT.  */
   if (alignment_in_bits > MAX_SUPPORTED_STACK_ALIGNMENT)
     {
-      /* If the required alignment exceeds MAX_SUPPORTED_STACK_ALIGNMENT and
-        it is not OK to reduce it.  Align the slot dynamically.  */
-      if (mode == BLKmode && (kind & ASLK_REDUCE_ALIGN) == 0)
-       dynamic_align_addr = true;
-      else
-       {
-         alignment_in_bits = MAX_SUPPORTED_STACK_ALIGNMENT;
-         alignment = MAX_SUPPORTED_STACK_ALIGNMENT / BITS_PER_UNIT;
-       }
+      alignment_in_bits = MAX_SUPPORTED_STACK_ALIGNMENT;
+      alignment = MAX_SUPPORTED_STACK_ALIGNMENT / BITS_PER_UNIT;
     }
 
-  if (SUPPORTS_STACK_ALIGNMENT && !dynamic_align_addr)
+  if (SUPPORTS_STACK_ALIGNMENT)
     {
       if (crtl->stack_alignment_estimated < alignment_in_bits)
        {
@@ -439,52 +437,20 @@ assign_stack_local_1 (machine_mode mode, poly_int64 size,
        }
     }
 
-  /* Handle overalignment here for parameter copy on the stack.
-     Reserved enough space for it and dynamically align the address.
-     No free frame_space is added here.  */
-  if (dynamic_align_addr)
-    {
-      rtx allocsize = gen_int_mode (size, Pmode);
-      get_dynamic_stack_size (&allocsize, 0, alignment_in_bits, NULL);
-
-      /* This is the size of space needed to accommodate required size of data
-        with given alignment.  */
-      poly_int64 len = rtx_to_poly_int64 (allocsize);
-      old_frame_offset = frame_offset;
-
-      if (FRAME_GROWS_DOWNWARD)
-       {
-         frame_offset -= len;
-         try_fit_stack_local (frame_offset, len, len,
-                              PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT,
-                              &slot_offset);
-       }
-      else
-       {
-         frame_offset += len;
-         try_fit_stack_local (old_frame_offset, len, len,
-                              PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT,
-                              &slot_offset);
-       }
-      goto found_space;
-    }
-  else
-    {
-      if (crtl->stack_alignment_needed < alignment_in_bits)
-       crtl->stack_alignment_needed = alignment_in_bits;
-      if (crtl->max_used_stack_slot_alignment < alignment_in_bits)
-       crtl->max_used_stack_slot_alignment = alignment_in_bits;
-    }
+  if (crtl->stack_alignment_needed < alignment_in_bits)
+    crtl->stack_alignment_needed = alignment_in_bits;
+  if (crtl->max_used_stack_slot_alignment < alignment_in_bits)
+    crtl->max_used_stack_slot_alignment = alignment_in_bits;
 
   if (mode != BLKmode || maybe_ne (size, 0))
     {
       if (kind & ASLK_RECORD_PAD)
        {
-         struct frame_space **psp;
+         class frame_space **psp;
 
          for (psp = &crtl->frame_space_list; *psp; psp = &(*psp)->next)
            {
-             struct frame_space *space = *psp;
+             class frame_space *space = *psp;
              if (!try_fit_stack_local (space->start, space->length, size,
                                        alignment, &slot_offset))
                continue;
@@ -561,12 +527,6 @@ assign_stack_local_1 (machine_mode mode, poly_int64 size,
                          (slot_offset + bigend_correction,
                           Pmode));
 
-  if (dynamic_align_addr)
-    {
-      addr = align_dynamic_address (addr, alignment_in_bits);
-      mark_reg_pointer (addr, alignment_in_bits);
-    }
-
   x = gen_rtx_MEM (mode, addr);
   set_mem_align (x, alignment_in_bits);
   MEM_NOTRAP_P (x) = 1;
@@ -601,11 +561,12 @@ assign_stack_local (machine_mode mode, poly_int64 size, int align)
    result, all temporaries are preserved.  A temporary is preserved by
    pretending it was allocated at the previous nesting level.  */
 
-struct GTY(()) temp_slot {
+class GTY(()) temp_slot {
+public:
   /* Points to next temporary slot.  */
-  struct temp_slot *next;
+  class temp_slot *next;
   /* Points to previous temporary slot.  */
-  struct temp_slot *prev;
+  class temp_slot *prev;
   /* The rtx to used to reference the slot.  */
   rtx slot;
   /* The size, in units, of the slot.  */
@@ -633,7 +594,7 @@ struct GTY(()) temp_slot {
 struct GTY((for_user)) temp_slot_address_entry {
   hashval_t hash;
   rtx address;
-  struct temp_slot *temp_slot;
+  class temp_slot *temp_slot;
 };
 
 struct temp_address_hasher : ggc_ptr_hash<temp_slot_address_entry>
@@ -650,7 +611,7 @@ static size_t n_temp_slots_in_use;
 /* Removes temporary slot TEMP from LIST.  */
 
 static void
-cut_slot_from_list (struct temp_slot *temp, struct temp_slot **list)
+cut_slot_from_list (class temp_slot *temp, class temp_slot **list)
 {
   if (temp->next)
     temp->next->prev = temp->prev;
@@ -665,7 +626,7 @@ cut_slot_from_list (struct temp_slot *temp, struct temp_slot **list)
 /* Inserts temporary slot TEMP to LIST.  */
 
 static void
-insert_slot_to_list (struct temp_slot *temp, struct temp_slot **list)
+insert_slot_to_list (class temp_slot *temp, class temp_slot **list)
 {
   temp->next = *list;
   if (*list)
@@ -676,11 +637,11 @@ insert_slot_to_list (struct temp_slot *temp, struct temp_slot **list)
 
 /* Returns the list of used temp slots at LEVEL.  */
 
-static struct temp_slot **
+static class temp_slot **
 temp_slots_at_level (int level)
 {
   if (level >= (int) vec_safe_length (used_temp_slots))
-    vec_safe_grow_cleared (used_temp_slots, level + 1);
+    vec_safe_grow_cleared (used_temp_slots, level + 1, true);
 
   return &(*used_temp_slots)[level];
 }
@@ -699,7 +660,7 @@ max_slot_level (void)
 /* Moves temporary slot TEMP to LEVEL.  */
 
 static void
-move_slot_to_level (struct temp_slot *temp, int level)
+move_slot_to_level (class temp_slot *temp, int level)
 {
   cut_slot_from_list (temp, temp_slots_at_level (temp->level));
   insert_slot_to_list (temp, temp_slots_at_level (level));
@@ -709,7 +670,7 @@ move_slot_to_level (struct temp_slot *temp, int level)
 /* Make temporary slot TEMP available.  */
 
 static void
-make_slot_available (struct temp_slot *temp)
+make_slot_available (class temp_slot *temp)
 {
   cut_slot_from_list (temp, temp_slots_at_level (temp->level));
   insert_slot_to_list (temp, &avail_temp_slots);
@@ -745,10 +706,10 @@ temp_address_hasher::equal (temp_slot_address_entry *t1,
 
 /* Add ADDRESS as an alias of TEMP_SLOT to the addess -> temp slot mapping.  */
 static void
-insert_temp_slot_address (rtx address, struct temp_slot *temp_slot)
+insert_temp_slot_address (rtx address, class temp_slot *temp_slot)
 {
   struct temp_slot_address_entry *t = ggc_alloc<temp_slot_address_entry> ();
-  t->address = address;
+  t->address = copy_rtx (address);
   t->temp_slot = temp_slot;
   t->hash = temp_slot_address_compute_hash (t);
   *temp_slot_address_table->find_slot_with_hash (t, t->hash, INSERT) = t;
@@ -779,10 +740,10 @@ remove_unused_temp_slot_addresses (void)
 
 /* Find the temp slot corresponding to the object at address X.  */
 
-static struct temp_slot *
+static class temp_slot *
 find_temp_slot_from_address (rtx x)
 {
-  struct temp_slot *p;
+  class temp_slot *p;
   struct temp_slot_address_entry tmp, *t;
 
   /* First try the easy way:
@@ -831,7 +792,7 @@ rtx
 assign_stack_temp_for_type (machine_mode mode, poly_int64 size, tree type)
 {
   unsigned int align;
-  struct temp_slot *p, *best_p = 0, *selected = NULL, **pp;
+  class temp_slot *p, *best_p = 0, *selected = NULL, **pp;
   rtx slot;
 
   gcc_assert (known_size_p (size));
@@ -1075,7 +1036,7 @@ assign_temp (tree type_or_decl, int memory_required,
 static void
 combine_temp_slots (void)
 {
-  struct temp_slot *p, *q, *next, *next_q;
+  class temp_slot *p, *q, *next, *next_q;
   int num_slots;
 
   /* We can't combine slots, because the information about which slot
@@ -1139,7 +1100,7 @@ combine_temp_slots (void)
 void
 update_temp_slot_address (rtx old_rtx, rtx new_rtx)
 {
-  struct temp_slot *p;
+  class temp_slot *p;
 
   if (rtx_equal_p (old_rtx, new_rtx))
     return;
@@ -1193,7 +1154,7 @@ update_temp_slot_address (rtx old_rtx, rtx new_rtx)
 void
 preserve_temp_slots (rtx x)
 {
-  struct temp_slot *p = 0, *next;
+  class temp_slot *p = 0, *next;
 
   if (x == 0)
     return;
@@ -1233,7 +1194,7 @@ preserve_temp_slots (rtx x)
 void
 free_temp_slots (void)
 {
-  struct temp_slot *p, *next;
+  class temp_slot *p, *next;
   bool some_available = false;
 
   for (p = *temp_slots_at_level (temp_slot_level); p; p = next)
@@ -1826,12 +1787,16 @@ instantiate_virtual_regs_in_insn (rtx_insn *insn)
        {
          error_for_asm (insn, "impossible constraint in %<asm%>");
          /* For asm goto, instead of fixing up all the edges
-            just clear the template and clear input operands
-            (asm goto doesn't have any output operands).  */
+            just clear the template and clear input and output operands
+            and strip away clobbers.  */
          if (JUMP_P (insn))
            {
              rtx asm_op = extract_asm_operands (PATTERN (insn));
+             PATTERN (insn) = asm_op;
+             PUT_MODE (asm_op, VOIDmode);
              ASM_OPERANDS_TEMPLATE (asm_op) = ggc_strdup ("");
+             ASM_OPERANDS_OUTPUT_CONSTRAINT (asm_op) = "";
+             ASM_OPERANDS_OUTPUT_IDX (asm_op) = 0;
              ASM_OPERANDS_INPUT_VEC (asm_op) = rtvec_alloc (0);
              ASM_OPERANDS_INPUT_CONSTRAINT_VEC (asm_op) = rtvec_alloc (0);
            }
@@ -2163,10 +2128,17 @@ aggregate_value_p (const_tree exp, const_tree fntype)
   if (!REG_P (reg))
     return 0;
 
+  /* Use the default ABI if the type of the function isn't known.
+     The scheme for handling interoperability between different ABIs
+     requires us to be able to tell when we're calling a function with
+     a nondefault ABI.  */
+  const predefined_function_abi &abi = (fntype
+                                       ? fntype_abi (fntype)
+                                       : default_function_abi);
   regno = REGNO (reg);
   nregs = hard_regno_nregs (regno, TYPE_MODE (type));
   for (i = 0; i < nregs; i++)
-    if (! call_used_regs[regno + i])
+    if (!fixed_regs[regno + i] && !abi.clobbers_full_reg_p (regno + i))
       return 1;
 
   return 0;
@@ -2240,13 +2212,15 @@ use_register_for_decl (const_tree decl)
       /* Otherwise, if RESULT_DECL is DECL_BY_REFERENCE, it will take
         the function_result_decl's assignment.  Since it's a pointer,
         we can short-circuit a number of the tests below, and we must
-        duplicat e them because we don't have the
-        function_result_decl to test.  */
+        duplicate them because we don't have the function_result_decl
+        to test.  */
       if (!targetm.calls.allocate_stack_slots_for_args ())
        return true;
       /* We don't set DECL_IGNORED_P for the function_result_decl.  */
       if (optimize)
        return true;
+      if (cfun->tail_call_marked)
+       return true;
       /* We don't set DECL_REGISTER for the function_result_decl.  */
       return false;
     }
@@ -2273,6 +2247,11 @@ use_register_for_decl (const_tree decl)
   if (optimize)
     return true;
 
+  /* Thunks force a tail call even at -O0 so we need to avoid creating a
+     dangling reference in case the parameter is passed by reference.  */
+  if (TREE_CODE (decl) == PARM_DECL && cfun->tail_call_marked)
+    return true;
+
   if (!DECL_REGISTER (decl))
     return false;
 
@@ -2308,18 +2287,13 @@ struct assign_parm_data_all
 struct assign_parm_data_one
 {
   tree nominal_type;
-  tree passed_type;
+  function_arg_info arg;
   rtx entry_parm;
   rtx stack_parm;
   machine_mode nominal_mode;
   machine_mode passed_mode;
-  machine_mode promoted_mode;
   struct locate_and_pad_arg_data locate;
   int partial;
-  BOOL_BITFIELD named_arg : 1;
-  BOOL_BITFIELD passed_pointer : 1;
-  BOOL_BITFIELD on_stack : 1;
-  BOOL_BITFIELD loaded_in_reg : 1;
 };
 
 /* A subroutine of assign_parms.  Initialize ALL.  */
@@ -2453,24 +2427,30 @@ static void
 assign_parm_find_data_types (struct assign_parm_data_all *all, tree parm,
                             struct assign_parm_data_one *data)
 {
-  tree nominal_type, passed_type;
-  machine_mode nominal_mode, passed_mode, promoted_mode;
   int unsignedp;
 
-  memset (data, 0, sizeof (*data));
+#ifndef BROKEN_VALUE_INITIALIZATION
+  *data = assign_parm_data_one ();
+#else
+  /* Old versions of GCC used to miscompile the above by only initializing
+     the members with explicit constructors and copying garbage
+     to the other members.  */
+  assign_parm_data_one zero_data = {};
+  *data = zero_data;
+#endif
 
   /* NAMED_ARG is a misnomer.  We really mean 'non-variadic'. */
   if (!cfun->stdarg)
-    data->named_arg = 1;  /* No variadic parms.  */
+    data->arg.named = 1;  /* No variadic parms.  */
   else if (DECL_CHAIN (parm))
-    data->named_arg = 1;  /* Not the last non-variadic parm. */
+    data->arg.named = 1;  /* Not the last non-variadic parm. */
   else if (targetm.calls.strict_argument_naming (all->args_so_far))
-    data->named_arg = 1;  /* Only variadic ones are unnamed.  */
+    data->arg.named = 1;  /* Only variadic ones are unnamed.  */
   else
-    data->named_arg = 0;  /* Treat as variadic.  */
+    data->arg.named = 0;  /* Treat as variadic.  */
 
-  nominal_type = TREE_TYPE (parm);
-  passed_type = DECL_ARG_TYPE (parm);
+  data->nominal_type = TREE_TYPE (parm);
+  data->arg.type = DECL_ARG_TYPE (parm);
 
   /* Look out for errors propagating this far.  Also, if the parameter's
      type is void then its value doesn't matter.  */
@@ -2478,47 +2458,38 @@ assign_parm_find_data_types (struct assign_parm_data_all *all, tree parm,
       /* This can happen after weird syntax errors
         or if an enum type is defined among the parms.  */
       || TREE_CODE (parm) != PARM_DECL
-      || passed_type == NULL
-      || VOID_TYPE_P (nominal_type))
+      || data->arg.type == NULL
+      || VOID_TYPE_P (data->nominal_type))
     {
-      nominal_type = passed_type = void_type_node;
-      nominal_mode = passed_mode = promoted_mode = VOIDmode;
-      goto egress;
+      data->nominal_type = data->arg.type = void_type_node;
+      data->nominal_mode = data->passed_mode = data->arg.mode = VOIDmode;
+      return;
     }
 
   /* Find mode of arg as it is passed, and mode of arg as it should be
      during execution of this function.  */
-  passed_mode = TYPE_MODE (passed_type);
-  nominal_mode = TYPE_MODE (nominal_type);
+  data->passed_mode = data->arg.mode = TYPE_MODE (data->arg.type);
+  data->nominal_mode = TYPE_MODE (data->nominal_type);
 
   /* If the parm is to be passed as a transparent union or record, use the
      type of the first field for the tests below.  We have already verified
      that the modes are the same.  */
-  if ((TREE_CODE (passed_type) == UNION_TYPE
-       || TREE_CODE (passed_type) == RECORD_TYPE)
-      && TYPE_TRANSPARENT_AGGR (passed_type))
-    passed_type = TREE_TYPE (first_field (passed_type));
+  if (RECORD_OR_UNION_TYPE_P (data->arg.type)
+      && TYPE_TRANSPARENT_AGGR (data->arg.type))
+    data->arg.type = TREE_TYPE (first_field (data->arg.type));
 
   /* See if this arg was passed by invisible reference.  */
-  if (pass_by_reference (&all->args_so_far_v, passed_mode,
-                        passed_type, data->named_arg))
+  if (apply_pass_by_reference_rules (&all->args_so_far_v, data->arg))
     {
-      passed_type = nominal_type = build_pointer_type (passed_type);
-      data->passed_pointer = true;
-      passed_mode = nominal_mode = TYPE_MODE (nominal_type);
+      data->nominal_type = data->arg.type;
+      data->passed_mode = data->nominal_mode = data->arg.mode;
     }
 
   /* Find mode as it is passed by the ABI.  */
-  unsignedp = TYPE_UNSIGNED (passed_type);
-  promoted_mode = promote_function_mode (passed_type, passed_mode, &unsignedp,
-                                        TREE_TYPE (current_function_decl), 0);
-
- egress:
-  data->nominal_type = nominal_type;
-  data->passed_type = passed_type;
-  data->nominal_mode = nominal_mode;
-  data->passed_mode = passed_mode;
-  data->promoted_mode = promoted_mode;
+  unsignedp = TYPE_UNSIGNED (data->arg.type);
+  data->arg.mode
+    = promote_function_mode (data->arg.type, data->arg.mode, &unsignedp,
+                            TREE_TYPE (current_function_decl), 0);
 }
 
 /* A subroutine of assign_parms.  Invoke setup_incoming_varargs.  */
@@ -2529,9 +2500,9 @@ assign_parms_setup_varargs (struct assign_parm_data_all *all,
 {
   int varargs_pretend_bytes = 0;
 
-  targetm.calls.setup_incoming_varargs (all->args_so_far,
-                                       data->promoted_mode,
-                                       data->passed_type,
+  function_arg_info last_named_arg = data->arg;
+  last_named_arg.named = true;
+  targetm.calls.setup_incoming_varargs (all->args_so_far, last_named_arg,
                                        &varargs_pretend_bytes, no_rtl);
 
   /* If the back-end has requested extra stack space, record how much is
@@ -2552,22 +2523,19 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
   rtx entry_parm;
   bool in_regs;
 
-  if (data->promoted_mode == VOIDmode)
+  if (data->arg.mode == VOIDmode)
     {
       data->entry_parm = data->stack_parm = const0_rtx;
       return;
     }
 
   targetm.calls.warn_parameter_passing_abi (all->args_so_far,
-                                           data->passed_type);
+                                           data->arg.type);
 
   entry_parm = targetm.calls.function_incoming_arg (all->args_so_far,
-                                                   data->promoted_mode,
-                                                   data->passed_type,
-                                                   data->named_arg);
-
+                                                   data->arg);
   if (entry_parm == 0)
-    data->promoted_mode = data->passed_mode;
+    data->arg.mode = data->passed_mode;
 
   /* Determine parm's home in the stack, in case it arrives in the stack
      or we should pretend it did.  Compute the stack position and rtx where
@@ -2583,32 +2551,29 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
 #ifdef STACK_PARMS_IN_REG_PARM_AREA
   in_regs = true;
 #endif
-  if (!in_regs && !data->named_arg)
+  if (!in_regs && !data->arg.named)
     {
       if (targetm.calls.pretend_outgoing_varargs_named (all->args_so_far))
        {
          rtx tem;
+         function_arg_info named_arg = data->arg;
+         named_arg.named = true;
          tem = targetm.calls.function_incoming_arg (all->args_so_far,
-                                                    data->promoted_mode,
-                                                    data->passed_type, true);
+                                                    named_arg);
          in_regs = tem != NULL;
        }
     }
 
   /* If this parameter was passed both in registers and in the stack, use
      the copy on the stack.  */
-  if (targetm.calls.must_pass_in_stack (data->promoted_mode,
-                                       data->passed_type))
+  if (targetm.calls.must_pass_in_stack (data->arg))
     entry_parm = 0;
 
   if (entry_parm)
     {
       int partial;
 
-      partial = targetm.calls.arg_partial_bytes (all->args_so_far,
-                                                data->promoted_mode,
-                                                data->passed_type,
-                                                data->named_arg);
+      partial = targetm.calls.arg_partial_bytes (all->args_so_far, data->arg);
       data->partial = partial;
 
       /* The caller might already have allocated stack space for the
@@ -2643,7 +2608,7 @@ assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
        }
     }
 
-  locate_and_pad_parm (data->promoted_mode, data->passed_type, in_regs,
+  locate_and_pad_parm (data->arg.mode, data->arg.type, in_regs,
                       all->reg_parm_stack_space,
                       entry_parm ? data->partial : 0, current_function_decl,
                       &all->stack_args_size, &data->locate);
@@ -2714,21 +2679,21 @@ assign_parm_find_stack_rtl (tree parm, struct assign_parm_data_one *data)
   stack_parm = crtl->args.internal_arg_pointer;
   if (offset_rtx != const0_rtx)
     stack_parm = gen_rtx_PLUS (Pmode, stack_parm, offset_rtx);
-  stack_parm = gen_rtx_MEM (data->promoted_mode, stack_parm);
+  stack_parm = gen_rtx_MEM (data->arg.mode, stack_parm);
 
-  if (!data->passed_pointer)
+  if (!data->arg.pass_by_reference)
     {
       set_mem_attributes (stack_parm, parm, 1);
       /* set_mem_attributes could set MEM_SIZE to the passed mode's size,
         while promoted mode's size is needed.  */
-      if (data->promoted_mode != BLKmode
-         && data->promoted_mode != DECL_MODE (parm))
+      if (data->arg.mode != BLKmode
+         && data->arg.mode != DECL_MODE (parm))
        {
-         set_mem_size (stack_parm, GET_MODE_SIZE (data->promoted_mode));
+         set_mem_size (stack_parm, GET_MODE_SIZE (data->arg.mode));
          if (MEM_EXPR (stack_parm) && MEM_OFFSET_KNOWN_P (stack_parm))
            {
              poly_int64 offset = subreg_lowpart_offset (DECL_MODE (parm),
-                                                        data->promoted_mode);
+                                                        data->arg.mode);
              if (maybe_ne (offset, 0))
                set_mem_offset (stack_parm, MEM_OFFSET (stack_parm) - offset);
            }
@@ -2743,8 +2708,23 @@ assign_parm_find_stack_rtl (tree parm, struct assign_parm_data_one *data)
      intentionally forcing upward padding.  Otherwise we have to come
      up with a guess at the alignment based on OFFSET_RTX.  */
   poly_int64 offset;
-  if (data->locate.where_pad != PAD_DOWNWARD || data->entry_parm)
+  if (data->locate.where_pad == PAD_NONE || data->entry_parm)
     align = boundary;
+  else if (data->locate.where_pad == PAD_UPWARD)
+    {
+      align = boundary;
+      /* If the argument offset is actually more aligned than the nominal
+        stack slot boundary, take advantage of that excess alignment.
+        Don't make any assumptions if STACK_POINTER_OFFSET is in use.  */
+      if (poly_int_rtx_p (offset_rtx, &offset)
+         && known_eq (STACK_POINTER_OFFSET, 0))
+       {
+         unsigned int offset_align = known_alignment (offset) * BITS_PER_UNIT;
+         if (offset_align == 0 || offset_align > STACK_BOUNDARY)
+           offset_align = STACK_BOUNDARY;
+         align = MAX (align, offset_align);
+       }
+    }
   else if (poly_int_rtx_p (offset_rtx, &offset))
     {
       align = least_bit_hwi (boundary);
@@ -2780,8 +2760,7 @@ assign_parm_adjust_entry_rtl (struct assign_parm_data_one *data)
         locations.  The Irix 6 ABI has examples of this.  */
       if (GET_CODE (entry_parm) == PARALLEL)
        emit_group_store (validize_mem (copy_rtx (stack_parm)), entry_parm,
-                         data->passed_type,
-                         int_size_in_bytes (data->passed_type));
+                         data->arg.type, int_size_in_bytes (data->arg.type));
       else
        {
          gcc_assert (data->partial % UNITS_PER_WORD == 0);
@@ -2837,7 +2816,7 @@ assign_parm_remove_parallels (struct assign_parm_data_one *data)
   if (GET_CODE (entry_parm) == PARALLEL && GET_MODE (entry_parm) != BLKmode)
     {
       rtx parmreg = gen_reg_rtx (GET_MODE (entry_parm));
-      emit_group_store (parmreg, entry_parm, data->passed_type,
+      emit_group_store (parmreg, entry_parm, data->arg.type,
                        GET_MODE_SIZE (GET_MODE (entry_parm)));
       entry_parm = parmreg;
     }
@@ -2857,8 +2836,11 @@ assign_parm_adjust_stack_rtl (struct assign_parm_data_one *data)
      ultimate type, don't use that slot after entry.  We'll make another
      stack slot, if we need one.  */
   if (stack_parm
-      && ((STRICT_ALIGNMENT
-          && GET_MODE_ALIGNMENT (data->nominal_mode) > MEM_ALIGN (stack_parm))
+      && ((GET_MODE_ALIGNMENT (data->nominal_mode) > MEM_ALIGN (stack_parm)
+          && ((optab_handler (movmisalign_optab, data->nominal_mode)
+               != CODE_FOR_nothing)
+              || targetm.slow_unaligned_access (data->nominal_mode,
+                                                MEM_ALIGN (stack_parm))))
          || (data->nominal_type
              && TYPE_ALIGN (data->nominal_type) > MEM_ALIGN (stack_parm)
              && MEM_ALIGN (stack_parm) < PREFERRED_STACK_BOUNDARY)))
@@ -2874,8 +2856,8 @@ assign_parm_adjust_stack_rtl (struct assign_parm_data_one *data)
   /* If stack protection is in effect for this function, don't leave any
      pointers in their passed stack slots.  */
   else if (crtl->stack_protect_guard
-          && (flag_stack_protect == 2
-              || data->passed_pointer
+          && (flag_stack_protect == SPCT_FLAG_ALL
+              || data->arg.pass_by_reference
               || POINTER_TYPE_P (data->nominal_type)))
     stack_parm = NULL;
 
@@ -2897,8 +2879,8 @@ assign_parm_setup_block_p (struct assign_parm_data_one *data)
   /* Only assign_parm_setup_block knows how to deal with register arguments
      that are padded at the least significant end.  */
   if (REG_P (data->entry_parm)
-      && known_lt (GET_MODE_SIZE (data->promoted_mode), UNITS_PER_WORD)
-      && (BLOCK_REG_PADDING (data->passed_mode, data->passed_type, 1)
+      && known_lt (GET_MODE_SIZE (data->arg.mode), UNITS_PER_WORD)
+      && (BLOCK_REG_PADDING (data->passed_mode, data->arg.type, 1)
          == (BYTES_BIG_ENDIAN ? PAD_UPWARD : PAD_DOWNWARD)))
     return true;
 #endif
@@ -2953,13 +2935,30 @@ assign_parm_setup_block (struct assign_parm_data_all *all,
       data->stack_parm = NULL;
     }
 
-  size = int_size_in_bytes (data->passed_type);
+  size = int_size_in_bytes (data->arg.type);
   size_stored = CEIL_ROUND (size, UNITS_PER_WORD);
   if (stack_parm == 0)
     {
-      SET_DECL_ALIGN (parm, MAX (DECL_ALIGN (parm), BITS_PER_WORD));
-      stack_parm = assign_stack_local (BLKmode, size_stored,
-                                      DECL_ALIGN (parm));
+      HOST_WIDE_INT parm_align
+       = (STRICT_ALIGNMENT
+          ? MAX (DECL_ALIGN (parm), BITS_PER_WORD) : DECL_ALIGN (parm));
+
+      SET_DECL_ALIGN (parm, parm_align);
+      if (DECL_ALIGN (parm) > MAX_SUPPORTED_STACK_ALIGNMENT)
+       {
+         rtx allocsize = gen_int_mode (size_stored, Pmode);
+         get_dynamic_stack_size (&allocsize, 0, DECL_ALIGN (parm), NULL);
+         stack_parm = assign_stack_local (BLKmode, UINTVAL (allocsize),
+                                          MAX_SUPPORTED_STACK_ALIGNMENT);
+         rtx addr = align_dynamic_address (XEXP (stack_parm, 0),
+                                           DECL_ALIGN (parm));
+         mark_reg_pointer (addr, DECL_ALIGN (parm));
+         stack_parm = gen_rtx_MEM (GET_MODE (stack_parm), addr);
+         MEM_NOTRAP_P (stack_parm) = 1;
+       }
+      else
+       stack_parm = assign_stack_local (BLKmode, size_stored,
+                                        DECL_ALIGN (parm));
       if (known_eq (GET_MODE_SIZE (GET_MODE (entry_parm)), size))
        PUT_MODE (stack_parm, GET_MODE (entry_parm));
       set_mem_attributes (stack_parm, parm, 1);
@@ -2991,12 +2990,12 @@ assign_parm_setup_block (struct assign_parm_data_all *all,
 
       /* Handle values in multiple non-contiguous locations.  */
       if (GET_CODE (entry_parm) == PARALLEL && !MEM_P (mem))
-       emit_group_store (mem, entry_parm, data->passed_type, size);
+       emit_group_store (mem, entry_parm, data->arg.type, size);
       else if (GET_CODE (entry_parm) == PARALLEL)
        {
          push_to_sequence2 (all->first_conversion_insn,
                             all->last_conversion_insn);
-         emit_group_store (mem, entry_parm, data->passed_type, size);
+         emit_group_store (mem, entry_parm, data->arg.type, size);
          all->first_conversion_insn = get_insns ();
          all->last_conversion_insn = get_last_insn ();
          end_sequence ();
@@ -3016,7 +3015,7 @@ assign_parm_setup_block (struct assign_parm_data_all *all,
          if (mode != BLKmode
 #ifdef BLOCK_REG_PADDING
              && (size == UNITS_PER_WORD
-                 || (BLOCK_REG_PADDING (mode, data->passed_type, 1)
+                 || (BLOCK_REG_PADDING (mode, data->arg.type, 1)
                      != (BYTES_BIG_ENDIAN ? PAD_UPWARD : PAD_DOWNWARD)))
 #endif
              )
@@ -3029,15 +3028,23 @@ assign_parm_setup_block (struct assign_parm_data_all *all,
                 to the value directly in mode MODE, otherwise we must
                 start with the register in word_mode and explicitly
                 convert it.  */
-             if (targetm.truly_noop_truncation (size * BITS_PER_UNIT,
-                                                BITS_PER_WORD))
+             if (mode == word_mode
+                 || TRULY_NOOP_TRUNCATION_MODES_P (mode, word_mode))
                reg = gen_rtx_REG (mode, REGNO (entry_parm));
              else
                {
                  reg = gen_rtx_REG (word_mode, REGNO (entry_parm));
                  reg = convert_to_mode (mode, copy_to_reg (reg), 1);
                }
-             emit_move_insn (change_address (mem, mode, 0), reg);
+
+             /* We use adjust_address to get a new MEM with the mode
+                changed.  adjust_address is better than change_address
+                for this purpose because adjust_address does not lose
+                the MEM_EXPR associated with the MEM.
+
+                If the MEM_EXPR is lost, then optimizations like DSE
+                assume the MEM escapes and thus is not subject to DSE.  */
+             emit_move_insn (adjust_address (mem, mode, 0), reg);
            }
 
 #ifdef BLOCK_REG_PADDING
@@ -3057,7 +3064,7 @@ assign_parm_setup_block (struct assign_parm_data_all *all,
                 additional changes to work correctly.  */
              gcc_checking_assert (BYTES_BIG_ENDIAN
                                   && (BLOCK_REG_PADDING (mode,
-                                                         data->passed_type, 1)
+                                                         data->arg.type, 1)
                                       == PAD_UPWARD));
 
              int by = (UNITS_PER_WORD - size) * BITS_PER_UNIT;
@@ -3078,7 +3085,7 @@ assign_parm_setup_block (struct assign_parm_data_all *all,
             handle all cases (e.g. SIZE == 3).  */
          else if (size != UNITS_PER_WORD
 #ifdef BLOCK_REG_PADDING
-                  && (BLOCK_REG_PADDING (mode, data->passed_type, 1)
+                  && (BLOCK_REG_PADDING (mode, data->arg.type, 1)
                       == PAD_DOWNWARD)
 #else
                   && BYTES_BIG_ENDIAN
@@ -3102,7 +3109,7 @@ assign_parm_setup_block (struct assign_parm_data_all *all,
          gcc_checking_assert (size > UNITS_PER_WORD);
 #ifdef BLOCK_REG_PADDING
          gcc_checking_assert (BLOCK_REG_PADDING (GET_MODE (mem),
-                                                 data->passed_type, 0)
+                                                 data->arg.type, 0)
                               == PAD_UPWARD);
 #endif
          emit_move_insn (mem, entry_parm);
@@ -3111,7 +3118,7 @@ assign_parm_setup_block (struct assign_parm_data_all *all,
        move_block_from_reg (REGNO (entry_parm), mem,
                             size_stored / UNITS_PER_WORD);
     }
-  else if (data->stack_parm == 0)
+  else if (data->stack_parm == 0 && !TYPE_EMPTY_P (data->arg.type))
     {
       push_to_sequence2 (all->first_conversion_insn, all->last_conversion_insn);
       emit_block_move (stack_parm, data->entry_parm, GEN_INT (size),
@@ -3155,6 +3162,7 @@ assign_parm_setup_reg (struct assign_parm_data_all *all, tree parm,
   int unsignedp = TYPE_UNSIGNED (TREE_TYPE (parm));
   bool did_conversion = false;
   bool need_conversion, moved;
+  enum insn_code icode;
   rtx rtl;
 
   /* Store the parm in a pseudoregister during the function, but we may
@@ -3170,9 +3178,9 @@ assign_parm_setup_reg (struct assign_parm_data_all *all, tree parm,
 
   /* If this was an item that we received a pointer to,
      set rtl appropriately.  */
-  if (data->passed_pointer)
+  if (data->arg.pass_by_reference)
     {
-      rtl = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (data->passed_type)), parmreg);
+      rtl = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (data->arg.type)), parmreg);
       set_mem_attributes (rtl, parm, 1);
     }
   else
@@ -3187,7 +3195,7 @@ assign_parm_setup_reg (struct assign_parm_data_all *all, tree parm,
   validated_mem = validize_mem (copy_rtx (data->entry_parm));
 
   need_conversion = (data->nominal_mode != data->passed_mode
-                    || promoted_nominal_mode != data->promoted_mode);
+                    || promoted_nominal_mode != data->arg.mode);
   moved = false;
 
   if (need_conversion
@@ -3216,7 +3224,6 @@ assign_parm_setup_reg (struct assign_parm_data_all *all, tree parm,
         conversion.  We verify that this insn does not clobber any
         hard registers.  */
 
-      enum insn_code icode;
       rtx op0, op1;
 
       icode = can_extend_p (promoted_nominal_mode, data->passed_mode,
@@ -3259,8 +3266,7 @@ assign_parm_setup_reg (struct assign_parm_data_all *all, tree parm,
          for (insn = insns; insn && moved; insn = NEXT_INSN (insn))
            {
              if (INSN_P (insn))
-               note_stores (PATTERN (insn), record_hard_reg_sets,
-                            &hardregs);
+               note_stores (insn, record_hard_reg_sets, &hardregs);
              if (!hard_reg_set_empty_p (hardregs))
                moved = false;
            }
@@ -3319,15 +3325,33 @@ assign_parm_setup_reg (struct assign_parm_data_all *all, tree parm,
 
       did_conversion = true;
     }
+  else if (MEM_P (data->entry_parm)
+          && GET_MODE_ALIGNMENT (promoted_nominal_mode)
+             > MEM_ALIGN (data->entry_parm)
+          && (((icode = optab_handler (movmisalign_optab,
+                                       promoted_nominal_mode))
+               != CODE_FOR_nothing)
+              || targetm.slow_unaligned_access (promoted_nominal_mode,
+                                                MEM_ALIGN (data->entry_parm))))
+    {
+      if (icode != CODE_FOR_nothing)
+       emit_insn (GEN_FCN (icode) (parmreg, validated_mem));
+      else
+       rtl = parmreg = extract_bit_field (validated_mem,
+                       GET_MODE_BITSIZE (promoted_nominal_mode), 0,
+                       unsignedp, parmreg,
+                       promoted_nominal_mode, VOIDmode, false, NULL);
+    }
   else
     emit_move_insn (parmreg, validated_mem);
 
-  /* If we were passed a pointer but the actual value can safely live
-     in a register, retrieve it and use it directly.  */
-  if (data->passed_pointer && TYPE_MODE (TREE_TYPE (parm)) != BLKmode)
+  /* If we were passed a pointer but the actual value can live in a register,
+     retrieve it and use it directly.  Note that we cannot use nominal_mode,
+     because it will have been set to Pmode above, we must use the actual mode
+     of the parameter instead.  */
+  if (data->arg.pass_by_reference && TYPE_MODE (TREE_TYPE (parm)) != BLKmode)
     {
-      /* We can't use nominal_mode, because it will have been set to
-        Pmode above.  We must use the actual mode of the parm.  */
+      /* Use a stack slot for debugging purposes if possible.  */
       if (use_register_for_decl (parm))
        {
          parmreg = gen_reg_rtx (TYPE_MODE (TREE_TYPE (parm)));
@@ -3441,7 +3465,7 @@ assign_parm_setup_stack (struct assign_parm_data_all *all, tree parm,
 
   assign_parm_remove_parallels (data);
 
-  if (data->promoted_mode != data->nominal_mode)
+  if (data->arg.mode != data->nominal_mode)
     {
       /* Conversion is required.  */
       rtx tempreg = gen_reg_rtx (GET_MODE (data->entry_parm));
@@ -3474,20 +3498,31 @@ assign_parm_setup_stack (struct assign_parm_data_all *all, tree parm,
 
       if (data->stack_parm == 0)
        {
-         int align = STACK_SLOT_ALIGNMENT (data->passed_type,
+         int align = STACK_SLOT_ALIGNMENT (data->arg.type,
                                            GET_MODE (data->entry_parm),
-                                           TYPE_ALIGN (data->passed_type));
+                                           TYPE_ALIGN (data->arg.type));
+         if (align < (int)GET_MODE_ALIGNMENT (GET_MODE (data->entry_parm))
+             && ((optab_handler (movmisalign_optab,
+                                 GET_MODE (data->entry_parm))
+                  != CODE_FOR_nothing)
+                 || targetm.slow_unaligned_access (GET_MODE (data->entry_parm),
+                                                   align)))
+           align = GET_MODE_ALIGNMENT (GET_MODE (data->entry_parm));
          data->stack_parm
            = assign_stack_local (GET_MODE (data->entry_parm),
                                  GET_MODE_SIZE (GET_MODE (data->entry_parm)),
                                  align);
+         align = MEM_ALIGN (data->stack_parm);
          set_mem_attributes (data->stack_parm, parm, 1);
+         set_mem_align (data->stack_parm, align);
        }
 
       dest = validize_mem (copy_rtx (data->stack_parm));
       src = validize_mem (copy_rtx (data->entry_parm));
 
-      if (MEM_P (src))
+      if (TYPE_EMPTY_P (data->arg.type))
+       /* Empty types don't really need to be copied.  */;
+      else if (MEM_P (src))
        {
          /* Use a block move to handle potentially misaligned entry_parm.  */
          if (!to_conversion)
@@ -3496,7 +3531,7 @@ assign_parm_setup_stack (struct assign_parm_data_all *all, tree parm,
          to_conversion = true;
 
          emit_block_move (dest, src,
-                          GEN_INT (int_size_in_bytes (data->passed_type)),
+                          GEN_INT (int_size_in_bytes (data->arg.type)),
                           BLOCK_OP_NORMAL);
        }
       else
@@ -3620,10 +3655,9 @@ assign_parms (tree fndecl)
       if (SUPPORTS_STACK_ALIGNMENT)
         {
           unsigned int align
-           = targetm.calls.function_arg_boundary (data.promoted_mode,
-                                                  data.passed_type);
-         align = MINIMUM_ALIGNMENT (data.passed_type, data.promoted_mode,
-                                    align);
+           = targetm.calls.function_arg_boundary (data.arg.mode,
+                                                  data.arg.type);
+         align = MINIMUM_ALIGNMENT (data.arg.type, data.arg.mode, align);
          if (TYPE_ALIGN (data.nominal_type) > align)
            align = MINIMUM_ALIGNMENT (data.nominal_type,
                                       TYPE_MODE (data.nominal_type),
@@ -3643,12 +3677,22 @@ assign_parms (tree fndecl)
        {
          assign_parm_find_stack_rtl (parm, &data);
          assign_parm_adjust_entry_rtl (&data);
+         /* For arguments that occupy no space in the parameter
+            passing area, have non-zero size and have address taken,
+            force creation of a stack slot so that they have distinct
+            address from other parameters.  */
+         if (TYPE_EMPTY_P (data.arg.type)
+             && TREE_ADDRESSABLE (parm)
+             && data.entry_parm == data.stack_parm
+             && MEM_P (data.entry_parm)
+             && int_size_in_bytes (data.arg.type))
+           data.stack_parm = NULL_RTX;
        }
       /* Record permanently how this parm was passed.  */
-      if (data.passed_pointer)
+      if (data.arg.pass_by_reference)
        {
          rtx incoming_rtl
-           = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (data.passed_type)),
+           = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (data.arg.type)),
                           data.entry_parm);
          set_decl_incoming_rtl (parm, incoming_rtl, true);
        }
@@ -3659,7 +3703,7 @@ assign_parms (tree fndecl)
 
       if (assign_parm_setup_block_p (&data))
        assign_parm_setup_block (&all, parm, &data);
-      else if (data.passed_pointer || use_register_for_decl (parm))
+      else if (data.arg.pass_by_reference || use_register_for_decl (parm))
        assign_parm_setup_reg (&all, parm, &data);
       else
        assign_parm_setup_stack (&all, parm, &data);
@@ -3668,8 +3712,7 @@ assign_parms (tree fndecl)
        assign_parms_setup_varargs (&all, &data, false);
 
       /* Update info on where next arg arrives in registers.  */
-      targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
-                                         data.passed_type, data.named_arg);
+      targetm.calls.function_arg_advance (all.args_so_far, data.arg);
     }
 
   if (targetm.calls.split_complex_arg)
@@ -3792,9 +3835,16 @@ assign_parms (tree fndecl)
        {
          rtx real_decl_rtl;
 
-         real_decl_rtl = targetm.calls.function_value (TREE_TYPE (decl_result),
-                                                       fndecl, true);
-         REG_FUNCTION_VALUE_P (real_decl_rtl) = 1;
+         /* Unless the psABI says not to.  */
+         if (TYPE_EMPTY_P (TREE_TYPE (decl_result)))
+           real_decl_rtl = NULL_RTX;
+         else
+           {
+             real_decl_rtl
+               = targetm.calls.function_value (TREE_TYPE (decl_result),
+                                               fndecl, true);
+             REG_FUNCTION_VALUE_P (real_decl_rtl) = 1;
+           }
          /* The delay slot scheduler assumes that crtl->return_rtx
             holds the hard register containing the return value, not a
             temporary pseudo.  */
@@ -3856,14 +3906,13 @@ gimplify_parameters (gimple_seq *cleanup)
        continue;
 
       /* Update info on where next arg arrives in registers.  */
-      targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
-                                         data.passed_type, data.named_arg);
+      targetm.calls.function_arg_advance (all.args_so_far, data.arg);
 
       /* ??? Once upon a time variable_size stuffed parameter list
         SAVE_EXPRs (amongst others) onto a pending sizes list.  This
         turned out to be less than manageable in the gimple world.
         Now we have to hunt them down ourselves.  */
-      walk_tree_without_duplicates (&data.passed_type,
+      walk_tree_without_duplicates (&data.arg.type,
                                    gimplify_parm_type, &stmts);
 
       if (TREE_CODE (DECL_SIZE_UNIT (parm)) != INTEGER_CST)
@@ -3872,11 +3921,11 @@ gimplify_parameters (gimple_seq *cleanup)
          gimplify_one_sizepos (&DECL_SIZE_UNIT (parm), &stmts);
        }
 
-      if (data.passed_pointer)
+      if (data.arg.pass_by_reference)
        {
-          tree type = TREE_TYPE (data.passed_type);
-         if (reference_callee_copied (&all.args_so_far_v, TYPE_MODE (type),
-                                      type, data.named_arg))
+         tree type = TREE_TYPE (data.arg.type);
+         function_arg_info orig_arg (type, data.arg.named);
+         if (reference_callee_copied (&all.args_so_far_v, orig_arg))
            {
              tree local, t;
 
@@ -3895,16 +3944,14 @@ gimplify_parameters (gimple_seq *cleanup)
                     as we'll query that flag during gimplification.  */
                  if (TREE_ADDRESSABLE (parm))
                    TREE_ADDRESSABLE (local) = 1;
-                 else if (TREE_CODE (type) == COMPLEX_TYPE
-                          || TREE_CODE (type) == VECTOR_TYPE)
-                   DECL_GIMPLE_REG_P (local) = 1;
+                 if (DECL_NOT_GIMPLE_REG_P (parm))
+                   DECL_NOT_GIMPLE_REG_P (local) = 1;
 
                  if (!is_gimple_reg (local)
                      && flag_stack_reuse != SR_NONE)
                    {
-                     tree clobber = build_constructor (type, NULL);
+                     tree clobber = build_clobber (type);
                      gimple *clobber_stmt;
-                     TREE_THIS_VOLATILE (clobber) = 1;
                      clobber_stmt = gimple_build_assign (local, clobber);
                      gimple_seq_add_stmt (cleanup, clobber_stmt);
                    }
@@ -4049,13 +4096,6 @@ locate_and_pad_parm (machine_mode passed_mode, tree type, int in_regs,
        }
     }
 
-  /* Remember if the outgoing parameter requires extra alignment on the
-     calling function side.  */
-  if (crtl->stack_alignment_needed < boundary)
-    crtl->stack_alignment_needed = boundary;
-  if (crtl->preferred_stack_boundary < boundary)
-    crtl->preferred_stack_boundary = boundary;
-
   if (ARGS_GROW_DOWNWARD)
     {
       locate->slot_offset.constant = -initial_offset_ptr->constant;
@@ -4655,7 +4695,8 @@ invoke_set_current_function_hook (tree fndecl)
       if (optimization_current_node != opts)
        {
          optimization_current_node = opts;
-         cl_optimization_restore (&global_options, TREE_OPTIMIZATION (opts));
+         cl_optimization_restore (&global_options, &global_options_set,
+                                  TREE_OPTIMIZATION (opts));
        }
 
       targetm.set_current_function (fndecl);
@@ -4735,6 +4776,16 @@ get_last_funcdef_no (void)
   return funcdef_no;
 }
 
+/* Allocate and initialize the stack usage info data structure for the
+   current function.  */
+static void
+allocate_stack_usage_info (void)
+{
+  gcc_assert (!cfun->su);
+  cfun->su = ggc_cleared_alloc<stack_usage> ();
+  cfun->su->static_stack_size = -1;
+}
+
 /* Allocate a function structure for FNDECL and set its contents
    to the defaults.  Set cfun to the newly-allocated object.
    Some of the helper functions invoked during initialization assume
@@ -4812,6 +4863,9 @@ allocate_struct_function (tree fndecl, bool abstract_p)
 
       if (!profile_flag && !flag_instrument_function_entry_exit)
        DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (fndecl) = 1;
+
+      if (flag_callgraph_info)
+       allocate_stack_usage_info ();
     }
 
   /* Don't enable begin stmt markers if var-tracking at assignments is
@@ -4819,6 +4873,8 @@ allocate_struct_function (tree fndecl, bool abstract_p)
      binding annotations among them.  */
   cfun->debug_nonbind_markers = lang_hooks.emits_begin_stmt
     && MAY_HAVE_DEBUG_MARKER_STMTS;
+
+  cfun->x_range_query = &global_ranges;
 }
 
 /* This is like allocate_struct_function, but pushes a new cfun for FNDECL
@@ -4844,17 +4900,20 @@ static void
 prepare_function_start (void)
 {
   gcc_assert (!get_last_insn ());
+
+  if (in_dummy_function)
+    crtl->abi = &default_function_abi;
+  else
+    crtl->abi = &fndecl_abi (cfun->decl).base_abi ();
+
   init_temp_slots ();
   init_emit ();
   init_varasm_status ();
   init_expr ();
   default_rtl_profile ();
 
-  if (flag_stack_usage_info)
-    {
-      cfun->su = ggc_cleared_alloc<stack_usage> ();
-      cfun->su->static_stack_size = -1;
-    }
+  if (flag_stack_usage_info && !flag_callgraph_info)
+    allocate_stack_usage_info ();
 
   cse_not_expected = ! optimize;
 
@@ -4890,6 +4949,9 @@ push_dummy_function (bool with_decl)
       fn_result_decl = build_decl (UNKNOWN_LOCATION, RESULT_DECL,
                                         NULL_TREE, void_type_node);
       DECL_RESULT (fn_decl) = fn_result_decl;
+      DECL_ARTIFICIAL (fn_decl) = 1;
+      tree fn_name = get_identifier (" ");
+      SET_DECL_ASSEMBLER_NAME (fn_decl, fn_name);
     }
   else
     fn_decl = NULL_TREE;
@@ -4934,21 +4996,37 @@ init_function_start (tree subr)
 void
 stack_protect_epilogue (void)
 {
-  tree guard_decl = targetm.stack_protect_guard ();
+  tree guard_decl = crtl->stack_protect_guard_decl;
   rtx_code_label *label = gen_label_rtx ();
   rtx x, y;
-  rtx_insn *seq;
+  rtx_insn *seq = NULL;
 
   x = expand_normal (crtl->stack_protect_guard);
-  if (guard_decl)
-    y = expand_normal (guard_decl);
+
+  if (targetm.have_stack_protect_combined_test () && guard_decl)
+    {
+      gcc_assert (DECL_P (guard_decl));
+      y = DECL_RTL (guard_decl);
+      /* Allow the target to compute address of Y and compare it with X without
+        leaking Y into a register.  This combined address + compare pattern
+        allows the target to prevent spilling of any intermediate results by
+        splitting it after register allocator.  */
+      seq = targetm.gen_stack_protect_combined_test (x, y, label);
+    }
   else
-    y = const0_rtx;
+    {
+      if (guard_decl)
+       y = expand_normal (guard_decl);
+      else
+       y = const0_rtx;
 
-  /* Allow the target to compare Y with X without leaking either into
-     a register.  */
-  if (targetm.have_stack_protect_test ()
-      && ((seq = targetm.gen_stack_protect_test (x, y, label)) != NULL_RTX))
+      /* Allow the target to compare Y with X without leaking either into
+        a register.  */
+      if (targetm.have_stack_protect_test ())
+       seq = targetm.gen_stack_protect_test (x, y, label);
+    }
+
+  if (seq)
     emit_insn (seq);
   else
     emit_cmp_and_jump_insns (x, y, EQ, NULL_RTX, ptr_mode, 1, label);
@@ -5154,7 +5232,7 @@ expand_function_start (tree subr)
       r_save = expand_expr (t_save, NULL_RTX, VOIDmode, EXPAND_WRITE);
       gcc_assert (GET_MODE (r_save) == Pmode);
 
-      emit_move_insn (r_save, targetm.builtin_setjmp_frame_value ());
+      emit_move_insn (r_save, hard_frame_pointer_rtx);
       update_nonlocal_goto_save_area ();
     }
 
@@ -5263,19 +5341,6 @@ use_return_register (void)
   diddle_return_value (do_use_return_reg, NULL);
 }
 
-/* Set the location of the insn chain starting at INSN to LOC.  */
-
-static void
-set_insn_locations (rtx_insn *insn, int loc)
-{
-  while (insn != NULL)
-    {
-      if (INSN_P (insn))
-       INSN_LOCATION (insn) = loc;
-      insn = NEXT_INSN (insn);
-    }
-}
-
 /* Generate RTL for the end of the current function.  */
 
 void
@@ -5341,19 +5406,17 @@ expand_function_end (void)
       if (flag_exceptions)
        sjlj_emit_function_exit_after (get_last_insn ());
     }
-  else
-    {
-      /* We want to ensure that instructions that may trap are not
-        moved into the epilogue by scheduling, because we don't
-        always emit unwind information for the epilogue.  */
-      if (cfun->can_throw_non_call_exceptions)
-       emit_insn (gen_blockage ());
-    }
 
   /* If this is an implementation of throw, do what's necessary to
      communicate between __builtin_eh_return and the epilogue.  */
   expand_eh_return ();
 
+  /* If stack protection is enabled for this function, check the guard.  */
+  if (crtl->stack_protect_guard
+      && targetm.stack_protect_runtime_enabled_p ()
+      && naked_return_label == NULL_RTX)
+    stack_protect_epilogue ();
+
   /* If scalar return value was computed in a pseudo-reg, or was a named
      return value that got dumped to the stack, copy that to the hard
      return register.  */
@@ -5362,9 +5425,11 @@ expand_function_end (void)
       tree decl_result = DECL_RESULT (current_function_decl);
       rtx decl_rtl = DECL_RTL (decl_result);
 
-      if (REG_P (decl_rtl)
-         ? REGNO (decl_rtl) >= FIRST_PSEUDO_REGISTER
-         : DECL_REGISTER (decl_result))
+      if ((REG_P (decl_rtl)
+          ? REGNO (decl_rtl) >= FIRST_PSEUDO_REGISTER
+          : DECL_REGISTER (decl_result))
+         /* Unless the psABI says not to.  */
+         && !TYPE_EMPTY_P (TREE_TYPE (decl_result)))
        {
          rtx real_decl_rtl = crtl->return_rtx;
          complex_mode cmode;
@@ -5500,7 +5565,9 @@ expand_function_end (void)
     emit_insn (gen_blockage ());
 
   /* If stack protection is enabled for this function, check the guard.  */
-  if (crtl->stack_protect_guard && targetm.stack_protect_runtime_enabled_p ())
+  if (crtl->stack_protect_guard
+      && targetm.stack_protect_runtime_enabled_p ()
+      && naked_return_label)
     stack_protect_epilogue ();
 
   /* If we had calls to alloca, and this machine needs
@@ -5780,6 +5847,107 @@ make_prologue_seq (void)
   return seq;
 }
 
+/* Emit a sequence of insns to zero the call-used registers before RET
+   according to ZERO_REGS_TYPE.  */
+
+static void
+gen_call_used_regs_seq (rtx_insn *ret, unsigned int zero_regs_type)
+{
+  bool only_gpr = true;
+  bool only_used = true;
+  bool only_arg = true;
+
+  /* No need to zero call-used-regs in main ().  */
+  if (MAIN_NAME_P (DECL_NAME (current_function_decl)))
+    return;
+
+  /* No need to zero call-used-regs if __builtin_eh_return is called
+     since it isn't a normal function return.  */
+  if (crtl->calls_eh_return)
+    return;
+
+  /* If only_gpr is true, only zero call-used registers that are
+     general-purpose registers; if only_used is true, only zero
+     call-used registers that are used in the current function;
+     if only_arg is true, only zero call-used registers that pass
+     parameters defined by the flatform's calling conversion.  */
+
+  using namespace zero_regs_flags;
+
+  only_gpr = zero_regs_type & ONLY_GPR;
+  only_used = zero_regs_type & ONLY_USED;
+  only_arg = zero_regs_type & ONLY_ARG;
+
+  /* For each of the hard registers, we should zero it if:
+           1. it is a call-used register;
+       and 2. it is not a fixed register;
+       and 3. it is not live at the return of the routine;
+       and 4. it is general registor if only_gpr is true;
+       and 5. it is used in the routine if only_used is true;
+       and 6. it is a register that passes parameter if only_arg is true.  */
+
+  /* First, prepare the data flow information.  */
+  basic_block bb = BLOCK_FOR_INSN (ret);
+  auto_bitmap live_out;
+  bitmap_copy (live_out, df_get_live_out (bb));
+  df_simulate_initialize_backwards (bb, live_out);
+  df_simulate_one_insn_backwards (bb, ret, live_out);
+
+  HARD_REG_SET selected_hardregs;
+  CLEAR_HARD_REG_SET (selected_hardregs);
+  for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    {
+      if (!crtl->abi->clobbers_full_reg_p (regno))
+       continue;
+      if (fixed_regs[regno])
+       continue;
+      if (REGNO_REG_SET_P (live_out, regno))
+       continue;
+      if (only_gpr
+         && !TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], regno))
+       continue;
+      if (only_used && !df_regs_ever_live_p (regno))
+       continue;
+      if (only_arg && !FUNCTION_ARG_REGNO_P (regno))
+       continue;
+#ifdef LEAF_REG_REMAP
+      if (crtl->uses_only_leaf_regs && LEAF_REG_REMAP (regno) < 0)
+       continue;
+#endif
+
+      /* Now this is a register that we might want to zero.  */
+      SET_HARD_REG_BIT (selected_hardregs, regno);
+    }
+
+  if (hard_reg_set_empty_p (selected_hardregs))
+    return;
+
+  /* Now that we have a hard register set that needs to be zeroed, pass it to
+     target to generate zeroing sequence.  */
+  HARD_REG_SET zeroed_hardregs;
+  start_sequence ();
+  zeroed_hardregs = targetm.calls.zero_call_used_regs (selected_hardregs);
+  rtx_insn *seq = get_insns ();
+  end_sequence ();
+  if (seq)
+    {
+      /* Emit the memory blockage and register clobber asm volatile before
+        the whole sequence.  */
+      start_sequence ();
+      expand_asm_reg_clobber_mem_blockage (zeroed_hardregs);
+      rtx_insn *seq_barrier = get_insns ();
+      end_sequence ();
+
+      emit_insn_before (seq_barrier, ret);
+      emit_insn_before (seq, ret);
+
+      /* Update the data flow information.  */
+      crtl->must_be_zero_on_return |= zeroed_hardregs;
+      df_set_bb_dirty (EXIT_BLOCK_PTR_FOR_FN (cfun));
+    }
+}
+
+
 /* Return a sequence to be used as the epilogue for the current function,
    or NULL.  */
 
@@ -6374,12 +6542,46 @@ rest_of_handle_thread_prologue_and_epilogue (void)
   cleanup_cfg (optimize ? CLEANUP_EXPENSIVE : 0);
 
   /* The stack usage info is finalized during prologue expansion.  */
-  if (flag_stack_usage_info)
+  if (flag_stack_usage_info || flag_callgraph_info)
     output_stack_usage ();
 
   return 0;
 }
 
+/* Record a final call to CALLEE at LOCATION.  */
+
+void
+record_final_call (tree callee, location_t location)
+{
+  struct callinfo_callee datum = { location, callee };
+  vec_safe_push (cfun->su->callees, datum);
+}
+
+/* Record a dynamic allocation made for DECL_OR_EXP.  */
+
+void
+record_dynamic_alloc (tree decl_or_exp)
+{
+  struct callinfo_dalloc datum;
+
+  if (DECL_P (decl_or_exp))
+    {
+      datum.location = DECL_SOURCE_LOCATION (decl_or_exp);
+      const char *name = lang_hooks.decl_printable_name (decl_or_exp, 2);
+      const char *dot = strrchr (name, '.');
+      if (dot)
+       name = dot + 1;
+      datum.name = ggc_strdup (name);
+    }
+  else
+    {
+      datum.location = EXPR_LOCATION (decl_or_exp);
+      datum.name = NULL;
+    }
+
+  vec_safe_push (cfun->su->dallocs, datum);
+}
+
 namespace {
 
 const pass_data pass_data_thread_prologue_and_epilogue =
@@ -6417,7 +6619,111 @@ make_pass_thread_prologue_and_epilogue (gcc::context *ctxt)
 {
   return new pass_thread_prologue_and_epilogue (ctxt);
 }
-\f
+
+namespace {
+
+const pass_data pass_data_zero_call_used_regs =
+{
+  RTL_PASS, /* type */
+  "zero_call_used_regs", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_zero_call_used_regs: public rtl_opt_pass
+{
+public:
+  pass_zero_call_used_regs (gcc::context *ctxt)
+    : rtl_opt_pass (pass_data_zero_call_used_regs, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual unsigned int execute (function *);
+
+}; // class pass_zero_call_used_regs
+
+unsigned int
+pass_zero_call_used_regs::execute (function *fun)
+{
+  using namespace zero_regs_flags;
+  unsigned int zero_regs_type = UNSET;
+
+  tree attr_zero_regs = lookup_attribute ("zero_call_used_regs",
+                                         DECL_ATTRIBUTES (fun->decl));
+
+  /* Get the type of zero_call_used_regs from function attribute.
+     We have filtered out invalid attribute values already at this point.  */
+  if (attr_zero_regs)
+    {
+      /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE
+        is the attribute argument's value.  */
+      attr_zero_regs = TREE_VALUE (attr_zero_regs);
+      gcc_assert (TREE_CODE (attr_zero_regs) == TREE_LIST);
+      attr_zero_regs = TREE_VALUE (attr_zero_regs);
+      gcc_assert (TREE_CODE (attr_zero_regs) == STRING_CST);
+
+      for (unsigned int i = 0; zero_call_used_regs_opts[i].name != NULL; ++i)
+       if (strcmp (TREE_STRING_POINTER (attr_zero_regs),
+                    zero_call_used_regs_opts[i].name) == 0)
+         {
+           zero_regs_type = zero_call_used_regs_opts[i].flag;
+           break;
+         }
+    }
+
+  if (!zero_regs_type)
+    zero_regs_type = flag_zero_call_used_regs;
+
+  /* No need to zero call-used-regs when no user request is present.  */
+  if (!(zero_regs_type & ENABLED))
+    return 0;
+
+  edge_iterator ei;
+  edge e;
+
+  /* This pass needs data flow information.  */
+  df_analyze ();
+
+  /* Iterate over the function's return instructions and insert any
+     register zeroing required by the -fzero-call-used-regs command-line
+     option or the "zero_call_used_regs" function attribute.  */
+  FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
+    {
+      rtx_insn *insn = BB_END (e->src);
+      if (JUMP_P (insn) && ANY_RETURN_P (JUMP_LABEL (insn)))
+       gen_call_used_regs_seq (insn, zero_regs_type);
+    }
+
+  return 0;
+}
+
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_zero_call_used_regs (gcc::context *ctxt)
+{
+  return new pass_zero_call_used_regs (ctxt);
+}
+
+/* If CONSTRAINT is a matching constraint, then return its number.
+   Otherwise, return -1.  */
+
+static int
+matching_constraint_num (const char *constraint)
+{
+  if (*constraint == '%')
+    constraint++;
+
+  if (IN_RANGE (*constraint, '0', '9'))
+    return strtoul (constraint, NULL, 10);
+
+  return -1;
+}
 
 /* This mini-pass fixes fall-out from SSA in asm statements that have
    in-out constraints.  Say you start with
@@ -6477,14 +6783,10 @@ match_asm_constraints_1 (rtx_insn *insn, rtx *p_sets, int noutputs)
       rtx input, output;
       rtx_insn *insns;
       const char *constraint = ASM_OPERANDS_INPUT_CONSTRAINT (op, i);
-      char *end;
       int match, j;
 
-      if (*constraint == '%')
-       constraint++;
-
-      match = strtoul (constraint, &end, 10);
-      if (end == constraint)
+      match = matching_constraint_num (constraint);
+      if (match < 0)
        continue;
 
       gcc_assert (match < noutputs);
@@ -6501,14 +6803,14 @@ match_asm_constraints_1 (rtx_insn *insn, rtx *p_sets, int noutputs)
       /* We can't do anything if the output is also used as input,
         as we're going to overwrite it.  */
       for (j = 0; j < ninputs; j++)
-        if (reg_overlap_mentioned_p (output, RTVEC_ELT (inputs, j)))
+       if (reg_overlap_mentioned_p (output, RTVEC_ELT (inputs, j)))
          break;
       if (j != ninputs)
        continue;
 
       /* Avoid changing the same input several times.  For
         asm ("" : "=mr" (out1), "=mr" (out2) : "0" (in), "1" (in));
-        only change in once (to out1), rather than changing it
+        only change it once (to out1), rather than changing it
         first to out1 and afterwards to out2.  */
       if (i > 0)
        {
@@ -6521,11 +6823,14 @@ match_asm_constraints_1 (rtx_insn *insn, rtx *p_sets, int noutputs)
       output_matched[match] = true;
 
       start_sequence ();
-      emit_move_insn (output, input);
+      emit_move_insn (output, copy_rtx (input));
       insns = get_insns ();
       end_sequence ();
       emit_insn_before (insns, insn);
 
+      constraint = ASM_OPERANDS_OUTPUT_CONSTRAINT(SET_SRC(p_sets[match]));
+      bool early_clobber_p = strchr (constraint, '&') != NULL;
+
       /* Now replace all mentions of the input with output.  We can't
         just replace the occurrence in inputs[i], as the register might
         also be used in some other input (or even in an address of an
@@ -6547,7 +6852,14 @@ match_asm_constraints_1 (rtx_insn *insn, rtx *p_sets, int noutputs)
         value, but different pseudos) where we formerly had only one.
         With more complicated asms this might lead to reload failures
         which wouldn't have happen without this pass.  So, iterate over
-        all operands and replace all occurrences of the register used.  */
+        all operands and replace all occurrences of the register used.
+
+        However, if one or more of the 'input' uses have a non-matching
+        constraint and the matched output operand is an early clobber
+        operand, then do not replace the input operand, since by definition
+        it conflicts with the output operand and cannot share the same
+        register.  See PR89313 for details.  */
+
       for (j = 0; j < noutputs; j++)
        if (!rtx_equal_p (SET_DEST (p_sets[j]), input)
            && reg_overlap_mentioned_p (input, SET_DEST (p_sets[j])))
@@ -6555,8 +6867,13 @@ match_asm_constraints_1 (rtx_insn *insn, rtx *p_sets, int noutputs)
                                              input, output);
       for (j = 0; j < ninputs; j++)
        if (reg_overlap_mentioned_p (input, RTVEC_ELT (inputs, j)))
-         RTVEC_ELT (inputs, j) = replace_rtx (RTVEC_ELT (inputs, j),
-                                              input, output);
+         {
+           if (!early_clobber_p
+               || match == matching_constraint_num
+                             (ASM_OPERANDS_INPUT_CONSTRAINT (op, j)))
+             RTVEC_ELT (inputs, j) = replace_rtx (RTVEC_ELT (inputs, j),
+                                                  input, output);
+         }
 
       changed = true;
     }