]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/dwarf2cfi.c
c++: Handle multiple aggregate overloads [PR95319].
[thirdparty/gcc.git] / gcc / dwarf2cfi.c
index 69218cb2dfec4a07501cc59f0f4da3bf1edd24a5..0d179b388e49256e31af13cf6a9fedd87cd53728 100644 (file)
@@ -1,7 +1,5 @@
 /* Dwarf2 Call Frame Information helper routines.
-   Copyright (C) 1992, 1993, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
-   Free Software Foundation, Inc.
+   Copyright (C) 1992-2020 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -22,24 +20,23 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "tm.h"
-#include "version.h"
-#include "flags.h"
-#include "rtl.h"
+#include "target.h"
 #include "function.h"
-#include "basic-block.h"
-#include "dwarf2.h"
+#include "rtl.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "memmodel.h"
+#include "tm_p.h"
+#include "emit-rtl.h"
+#include "stor-layout.h"
+#include "cfgbuild.h"
 #include "dwarf2out.h"
 #include "dwarf2asm.h"
-#include "ggc.h"
-#include "tm_p.h"
-#include "target.h"
 #include "common/common-target.h"
-#include "tree-pass.h"
 
 #include "except.h"            /* expand_builtin_dwarf_sp_column */
+#include "profile-count.h"     /* For expr.h */
 #include "expr.h"              /* init_return_column_size */
-#include "regs.h"              /* expand_builtin_init_dwarf_reg_sizes */
 #include "output.h"            /* asm_out_file */
 #include "debug.h"             /* dwarf2out_do_frame, dwarf2out_do_cfi_asm */
 
@@ -56,11 +53,12 @@ along with GCC; see the file COPYING3.  If not see
 #define INCOMING_RETURN_ADDR_RTX  (gcc_unreachable (), NULL_RTX)
 #endif
 
-/* Maximum size (in bytes) of an artificially generated label.  */
-#define MAX_ARTIFICIAL_LABEL_BYTES     30
+#ifndef DEFAULT_INCOMING_FRAME_SP_OFFSET
+#define DEFAULT_INCOMING_FRAME_SP_OFFSET INCOMING_FRAME_SP_OFFSET
+#endif
 \f
 /* A collected description of an entire row of the abstract CFI table.  */
-typedef struct GTY(()) dw_cfi_row_struct
+struct GTY(()) dw_cfi_row
 {
   /* The expression that computes the CFA, expressed in two different ways.
      The CFA member for the simple cases, and the full CFI expression for
@@ -71,18 +69,19 @@ typedef struct GTY(()) dw_cfi_row_struct
   /* The expressions for any register column that is saved.  */
   cfi_vec reg_save;
 
-  /* The value of any DW_CFA_GNU_args_size.  */
-  HOST_WIDE_INT args_size;
-} dw_cfi_row;
+  /* True if the register window is saved.  */
+  bool window_save;
+
+  /* True if the return address is in a mangled state.  */
+  bool ra_mangled;
+};
 
 /* The caller's ORIG_REG is saved in SAVED_IN_REG.  */
-typedef struct GTY(()) reg_saved_in_data_struct {
+struct GTY(()) reg_saved_in_data {
   rtx orig_reg;
   rtx saved_in_reg;
-} reg_saved_in_data;
+};
 
-DEF_VEC_O (reg_saved_in_data);
-DEF_VEC_ALLOC_O (reg_saved_in_data, heap);
 
 /* Since we no longer have a proper CFG, we're going to create a facsimile
    of one on the fly while processing the frame-related insns.
@@ -101,14 +100,24 @@ DEF_VEC_ALLOC_O (reg_saved_in_data, heap);
    All save points are present in the TRACE_INDEX hash, mapping the insn
    starting a trace to the dw_trace_info describing the trace.  */
 
-typedef struct
+struct dw_trace_info
 {
   /* The insn that begins the trace.  */
-  rtx head;
+  rtx_insn *head;
 
   /* The row state at the beginning and end of the trace.  */
   dw_cfi_row *beg_row, *end_row;
 
+  /* Tracking for DW_CFA_GNU_args_size.  The "true" sizes are those we find
+     while scanning insns.  However, the args_size value is irrelevant at
+     any point except can_throw_internal_p insns.  Therefore the "delay"
+     sizes the values that must actually be emitted for this trace.  */
+  poly_int64_pod beg_true_args_size, end_true_args_size;
+  poly_int64_pod beg_delay_args_size, end_delay_args_size;
+
+  /* The first EH insn in the trace, where beg_delay_args_size must be set.  */
+  rtx_insn *eh_head;
+
   /* The following variables contain data used in interpreting frame related
      expressions.  These are not part of the "real" row state as defined by
      Dwarf, but it seems like they need to be propagated into a trace in case
@@ -134,27 +143,47 @@ typedef struct
      implemented as a flat array because it normally contains zero or 1
      entry, depending on the target.  IA-64 is the big spender here, using
      a maximum of 5 entries.  */
-  VEC(reg_saved_in_data, heap) *regs_saved_in_regs;
+  vec<reg_saved_in_data> regs_saved_in_regs;
 
   /* An identifier for this trace.  Used only for debugging dumps.  */
   unsigned id;
 
   /* True if this trace immediately follows NOTE_INSN_SWITCH_TEXT_SECTIONS.  */
   bool switch_sections;
-} dw_trace_info;
 
-DEF_VEC_O (dw_trace_info);
-DEF_VEC_ALLOC_O (dw_trace_info, heap);
+  /* True if we've seen different values incoming to beg_true_args_size.  */
+  bool args_size_undefined;
 
-typedef dw_trace_info *dw_trace_info_ref;
+  /* True if we've seen an insn with a REG_ARGS_SIZE note before EH_HEAD.  */
+  bool args_size_defined_for_eh;
+};
+
+
+/* Hashtable helpers.  */
+
+struct trace_info_hasher : nofree_ptr_hash <dw_trace_info>
+{
+  static inline hashval_t hash (const dw_trace_info *);
+  static inline bool equal (const dw_trace_info *, const dw_trace_info *);
+};
+
+inline hashval_t
+trace_info_hasher::hash (const dw_trace_info *ti)
+{
+  return INSN_UID (ti->head);
+}
+
+inline bool
+trace_info_hasher::equal (const dw_trace_info *a, const dw_trace_info *b)
+{
+  return a->head == b->head;
+}
 
-DEF_VEC_P (dw_trace_info_ref);
-DEF_VEC_ALLOC_P (dw_trace_info_ref, heap);
 
 /* The variables making up the pseudo-cfg, as described above.  */
-static VEC (dw_trace_info, heap) *trace_info;
-static VEC (dw_trace_info_ref, heap) *trace_work_list;
-static htab_t trace_index;
+static vec<dw_trace_info> trace_info;
+static vec<dw_trace_info *> trace_work_list;
+static hash_table<trace_info_hasher> *trace_index;
 
 /* A vector of call frame insns for the CIE.  */
 cfi_vec cie_cfi_vec;
@@ -168,7 +197,7 @@ static GTY(()) reg_saved_in_data *cie_return_save;
 static GTY(()) unsigned long dwarf2out_cfi_label_num;
 
 /* The insn after which a new CFI note should be emitted.  */
-static rtx add_cfi_insn;
+static rtx_insn *add_cfi_insn;
 
 /* When non-null, add_cfi will add the CFI to this vector.  */
 static cfi_vec *add_cfi_vec;
@@ -179,24 +208,22 @@ static dw_trace_info *cur_trace;
 /* The current, i.e. most recently generated, row of the CFI table.  */
 static dw_cfi_row *cur_row;
 
+/* A copy of the current CFA, for use during the processing of a
+   single insn.  */
+static dw_cfa_location *cur_cfa;
+
 /* We delay emitting a register save until either (a) we reach the end
    of the prologue or (b) the register is clobbered.  This clusters
    register saves so that there are fewer pc advances.  */
 
-typedef struct {
+struct queued_reg_save {
   rtx reg;
   rtx saved_reg;
-  HOST_WIDE_INT cfa_offset;
-} queued_reg_save;
-
-DEF_VEC_O (queued_reg_save);
-DEF_VEC_ALLOC_O (queued_reg_save, heap);
+  poly_int64_pod cfa_offset;
+};
 
-static VEC(queued_reg_save, heap) *queued_reg_saves;
 
-/* The (really) current value for DW_CFA_GNU_args_size.  We delay actually
-   emitting this data, i.e. updating CUR_ROW, without async unwind.  */
-static HOST_WIDE_INT queued_args_size;
+static vec<queued_reg_save> queued_reg_saves;
 
 /* True if any CFI directives were emitted at the current insn.  */
 static bool any_cfis_emitted;
@@ -218,53 +245,112 @@ expand_builtin_dwarf_sp_column (void)
    which has mode MODE.  Initialize column C as a return address column.  */
 
 static void
-init_return_column_size (enum machine_mode mode, rtx mem, unsigned int c)
+init_return_column_size (scalar_int_mode mode, rtx mem, unsigned int c)
 {
   HOST_WIDE_INT offset = c * GET_MODE_SIZE (mode);
   HOST_WIDE_INT size = GET_MODE_SIZE (Pmode);
-  emit_move_insn (adjust_address (mem, mode, offset), GEN_INT (size));
+  emit_move_insn (adjust_address (mem, mode, offset),
+                 gen_int_mode (size, mode));
+}
+
+/* Datastructure used by expand_builtin_init_dwarf_reg_sizes and
+   init_one_dwarf_reg_size to communicate on what has been done by the
+   latter.  */
+
+struct init_one_dwarf_reg_state
+{
+  /* Whether the dwarf return column was initialized.  */
+  bool wrote_return_column;
+
+  /* For each hard register REGNO, whether init_one_dwarf_reg_size
+     was given REGNO to process already.  */
+  bool processed_regno [FIRST_PSEUDO_REGISTER];
+
+};
+
+/* Helper for expand_builtin_init_dwarf_reg_sizes.  Generate code to
+   initialize the dwarf register size table entry corresponding to register
+   REGNO in REGMODE.  TABLE is the table base address, SLOTMODE is the mode to
+   use for the size entry to initialize, and INIT_STATE is the communication
+   datastructure conveying what we're doing to our caller.  */
+
+static
+void init_one_dwarf_reg_size (int regno, machine_mode regmode,
+                             rtx table, machine_mode slotmode,
+                             init_one_dwarf_reg_state *init_state)
+{
+  const unsigned int dnum = DWARF_FRAME_REGNUM (regno);
+  const unsigned int rnum = DWARF2_FRAME_REG_OUT (dnum, 1);
+  const unsigned int dcol = DWARF_REG_TO_UNWIND_COLUMN (rnum);
+  
+  poly_int64 slotoffset = dcol * GET_MODE_SIZE (slotmode);
+  poly_int64 regsize = GET_MODE_SIZE (regmode);
+
+  init_state->processed_regno[regno] = true;
+
+  if (rnum >= DWARF_FRAME_REGISTERS)
+    return;
+
+  if (dnum == DWARF_FRAME_RETURN_COLUMN)
+    {
+      if (regmode == VOIDmode)
+       return;
+      init_state->wrote_return_column = true;
+    }
+
+  /* ??? When is this true?  Should it be a test based on DCOL instead?  */
+  if (maybe_lt (slotoffset, 0))
+    return;
+
+  emit_move_insn (adjust_address (table, slotmode, slotoffset),
+                 gen_int_mode (regsize, slotmode));
 }
 
-/* Generate code to initialize the register size table.  */
+/* Generate code to initialize the dwarf register size table located
+   at the provided ADDRESS.  */
 
 void
 expand_builtin_init_dwarf_reg_sizes (tree address)
 {
   unsigned int i;
-  enum machine_mode mode = TYPE_MODE (char_type_node);
+  scalar_int_mode mode = SCALAR_INT_TYPE_MODE (char_type_node);
   rtx addr = expand_normal (address);
   rtx mem = gen_rtx_MEM (BLKmode, addr);
-  bool wrote_return_column = false;
+
+  init_one_dwarf_reg_state init_state;
+
+  memset ((char *)&init_state, 0, sizeof (init_state));
 
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     {
-      unsigned int dnum = DWARF_FRAME_REGNUM (i);
-      unsigned int rnum = DWARF2_FRAME_REG_OUT (dnum, 1);
+      machine_mode save_mode;
+      rtx span;
 
-      if (rnum < DWARF_FRAME_REGISTERS)
-       {
-         HOST_WIDE_INT offset = rnum * GET_MODE_SIZE (mode);
-         enum machine_mode save_mode = reg_raw_mode[i];
-         HOST_WIDE_INT size;
+      /* No point in processing a register multiple times.  This could happen
+        with register spans, e.g. when a reg is first processed as a piece of
+        a span, then as a register on its own later on.  */
+
+      if (init_state.processed_regno[i])
+       continue;
 
-         if (HARD_REGNO_CALL_PART_CLOBBERED (i, save_mode))
-           save_mode = choose_hard_reg_mode (i, 1, true);
-         if (dnum == DWARF_FRAME_RETURN_COLUMN)
+      save_mode = targetm.dwarf_frame_reg_mode (i);
+      span = targetm.dwarf_register_span (gen_rtx_REG (save_mode, i));
+
+      if (!span)
+       init_one_dwarf_reg_size (i, save_mode, mem, mode, &init_state);
+      else
+       {
+         for (int si = 0; si < XVECLEN (span, 0); si++)
            {
-             if (save_mode == VOIDmode)
-               continue;
-             wrote_return_column = true;
-           }
-         size = GET_MODE_SIZE (save_mode);
-         if (offset < 0)
-           continue;
+             rtx reg = XVECEXP (span, 0, si);
 
-         emit_move_insn (adjust_address (mem, mode, offset),
-                         gen_int_mode (size, mode));
+             init_one_dwarf_reg_size
+               (REGNO (reg), GET_MODE (reg), mem, mode, &init_state);
+           }
        }
     }
 
-  if (!wrote_return_column)
+  if (!init_state.wrote_return_column)
     init_return_column_size (mode, mem, DWARF_FRAME_RETURN_COLUMN);
 
 #ifdef DWARF_ALT_FRAME_RETURN_COLUMN
@@ -275,32 +361,16 @@ expand_builtin_init_dwarf_reg_sizes (tree address)
 }
 
 \f
-static hashval_t
-dw_trace_info_hash (const void *ptr)
-{
-  const dw_trace_info *ti = (const dw_trace_info *) ptr;
-  return INSN_UID (ti->head);
-}
-
-static int
-dw_trace_info_eq (const void *ptr_a, const void *ptr_b)
-{
-  const dw_trace_info *a = (const dw_trace_info *) ptr_a;
-  const dw_trace_info *b = (const dw_trace_info *) ptr_b;
-  return a->head == b->head;
-}
-
 static dw_trace_info *
-get_trace_info (rtx insn)
+get_trace_info (rtx_insn *insn)
 {
   dw_trace_info dummy;
   dummy.head = insn;
-  return (dw_trace_info *)
-    htab_find_with_hash (trace_index, &dummy, INSN_UID (insn));
+  return trace_index->find_with_hash (&dummy, INSN_UID (insn));
 }
 
 static bool
-save_point_p (rtx insn)
+save_point_p (rtx_insn *insn)
 {
   /* Labels, except those that are really jump tables.  */
   if (LABEL_P (insn))
@@ -345,7 +415,7 @@ need_data_align_sf_opcode (HOST_WIDE_INT off)
 static inline dw_cfi_ref
 new_cfi (void)
 {
-  dw_cfi_ref cfi = ggc_alloc_dw_cfi_node ();
+  dw_cfi_ref cfi = ggc_alloc<dw_cfi_node> ();
 
   cfi->dw_cfi_oprnd1.dw_cfi_reg_num = 0;
   cfi->dw_cfi_oprnd2.dw_cfi_reg_num = 0;
@@ -358,7 +428,7 @@ new_cfi (void)
 static dw_cfi_row *
 new_cfi_row (void)
 {
-  dw_cfi_row *row = ggc_alloc_cleared_dw_cfi_row ();
+  dw_cfi_row *row = ggc_cleared_alloc<dw_cfi_row> ();
 
   row->cfa.reg = INVALID_REGNUM;
 
@@ -370,11 +440,21 @@ new_cfi_row (void)
 static dw_cfi_row *
 copy_cfi_row (dw_cfi_row *src)
 {
-  dw_cfi_row *dst = ggc_alloc_dw_cfi_row ();
+  dw_cfi_row *dst = ggc_alloc<dw_cfi_row> ();
 
   *dst = *src;
-  dst->reg_save = VEC_copy (dw_cfi_ref, gc, src->reg_save);
+  dst->reg_save = vec_safe_copy (src->reg_save);
+
+  return dst;
+}
 
+/* Return a copy of an existing CFA location.  */
+
+static dw_cfa_location *
+copy_cfa (dw_cfa_location *src)
+{
+  dw_cfa_location *dst = ggc_alloc<dw_cfa_location> ();
+  *dst = *src;
   return dst;
 }
 
@@ -405,16 +485,23 @@ add_cfi (dw_cfi_ref cfi)
     }
 
   if (add_cfi_vec != NULL)
-    VEC_safe_push (dw_cfi_ref, gc, *add_cfi_vec, cfi);
+    vec_safe_push (*add_cfi_vec, cfi);
 }
 
 static void
-add_cfi_args_size (HOST_WIDE_INT size)
+add_cfi_args_size (poly_int64 size)
 {
+  /* We don't yet have a representation for polynomial sizes.  */
+  HOST_WIDE_INT const_size = size.to_constant ();
+
   dw_cfi_ref cfi = new_cfi ();
 
+  /* While we can occasionally have args_size < 0 internally, this state
+     should not persist at a point we actually need an opcode.  */
+  gcc_assert (const_size >= 0);
+
   cfi->dw_cfi_opc = DW_CFA_GNU_args_size;
-  cfi->dw_cfi_oprnd1.dw_cfi_offset = size;
+  cfi->dw_cfi_oprnd1.dw_cfi_offset = const_size;
 
   add_cfi (cfi);
 }
@@ -436,18 +523,18 @@ add_cfi_restore (unsigned reg)
 static void
 update_row_reg_save (dw_cfi_row *row, unsigned column, dw_cfi_ref cfi)
 {
-  if (VEC_length (dw_cfi_ref, row->reg_save) <= column)
-    VEC_safe_grow_cleared (dw_cfi_ref, gc, row->reg_save, column + 1);
-  VEC_replace (dw_cfi_ref, row->reg_save, column, cfi);
+  if (vec_safe_length (row->reg_save) <= column)
+    vec_safe_grow_cleared (row->reg_save, column + 1);
+  (*row->reg_save)[column] = cfi;
 }
 
 /* This function fills in aa dw_cfa_location structure from a dwarf location
    descriptor sequence.  */
 
 static void
-get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_struct *loc)
+get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
 {
-  struct dw_loc_descr_struct *ptr;
+  struct dw_loc_descr_node *ptr;
   cfa->offset = 0;
   cfa->base_offset = 0;
   cfa->indirect = 0;
@@ -569,7 +656,10 @@ lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_location *loc, dw_cfa_location *remember)
       loc->offset = cfi->dw_cfi_oprnd2.dw_cfi_offset;
       break;
     case DW_CFA_def_cfa_expression:
-      get_cfa_from_loc_descr (loc, cfi->dw_cfi_oprnd1.dw_cfi_loc);
+      if (cfi->dw_cfi_oprnd2.dw_cfi_cfa_loc)
+       *loc = *cfi->dw_cfi_oprnd2.dw_cfi_cfa_loc;
+      else
+       get_cfa_from_loc_descr (loc, cfi->dw_cfi_oprnd1.dw_cfi_loc);
       break;
 
     case DW_CFA_remember_state:
@@ -594,10 +684,10 @@ bool
 cfa_equal_p (const dw_cfa_location *loc1, const dw_cfa_location *loc2)
 {
   return (loc1->reg == loc2->reg
-         && loc1->offset == loc2->offset
+         && known_eq (loc1->offset, loc2->offset)
          && loc1->indirect == loc2->indirect
          && (loc1->indirect == 0
-             || loc1->base_offset == loc2->base_offset));
+             || known_eq (loc1->base_offset, loc2->base_offset)));
 }
 
 /* Determine if two CFI operands are identical.  */
@@ -618,6 +708,8 @@ cfi_oprnd_equal_p (enum dw_cfi_oprnd_type t, dw_cfi_oprnd *a, dw_cfi_oprnd *b)
              || strcmp (a->dw_cfi_addr, b->dw_cfi_addr) == 0);
     case dw_cfi_oprnd_loc:
       return loc_descr_equal_p (a->dw_cfi_loc, b->dw_cfi_loc);
+    case dw_cfi_oprnd_cfa_loc:
+      return cfa_equal_p (a->dw_cfi_cfa_loc, b->dw_cfi_cfa_loc);
     }
   gcc_unreachable ();
 }
@@ -663,18 +755,8 @@ cfi_row_equal_p (dw_cfi_row *a, dw_cfi_row *b)
   else if (!cfa_equal_p (&a->cfa, &b->cfa))
     return false;
 
-  /* Logic suggests that we compare args_size here.  However, if
-     EXIT_IGNORE_STACK we don't bother tracking the args_size after
-     the last time it really matters within the function.  This does
-     in fact lead to paths with differing arg_size, but in cases for
-     which it doesn't matter.  */
-  /* ??? If we really want to sanity check the output of the optimizers,
-     find a way to backtrack from epilogues to the last EH site.  This
-     would allow us to distinguish regions with garbage args_size and
-     regions where paths ought to agree.  */
-
-  n_a = VEC_length (dw_cfi_ref, a->reg_save);
-  n_b = VEC_length (dw_cfi_ref, b->reg_save);
+  n_a = vec_safe_length (a->reg_save);
+  n_b = vec_safe_length (b->reg_save);
   n_max = MAX (n_a, n_b);
 
   for (i = 0; i < n_max; ++i)
@@ -682,14 +764,20 @@ cfi_row_equal_p (dw_cfi_row *a, dw_cfi_row *b)
       dw_cfi_ref r_a = NULL, r_b = NULL;
 
       if (i < n_a)
-       r_a = VEC_index (dw_cfi_ref, a->reg_save, i);
+       r_a = (*a->reg_save)[i];
       if (i < n_b)
-       r_b = VEC_index (dw_cfi_ref, b->reg_save, i);
+       r_b = (*b->reg_save)[i];
 
       if (!cfi_equal_p (r_a, r_b))
         return false;
     }
 
+  if (a->window_save != b->window_save)
+    return false;
+
+  if (a->ra_mangled != b->ra_mangled)
+    return false;
+
   return true;
 }
 
@@ -708,57 +796,68 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
 
   cfi = new_cfi ();
 
-  if (new_cfa->reg == old_cfa->reg && !new_cfa->indirect && !old_cfa->indirect)
+  HOST_WIDE_INT const_offset;
+  if (new_cfa->reg == old_cfa->reg
+      && !new_cfa->indirect
+      && !old_cfa->indirect
+      && new_cfa->offset.is_constant (&const_offset))
     {
       /* Construct a "DW_CFA_def_cfa_offset <offset>" instruction, indicating
         the CFA register did not change but the offset did.  The data
         factoring for DW_CFA_def_cfa_offset_sf happens in output_cfi, or
         in the assembler via the .cfi_def_cfa_offset directive.  */
-      if (new_cfa->offset < 0)
+      if (const_offset < 0)
        cfi->dw_cfi_opc = DW_CFA_def_cfa_offset_sf;
       else
        cfi->dw_cfi_opc = DW_CFA_def_cfa_offset;
-      cfi->dw_cfi_oprnd1.dw_cfi_offset = new_cfa->offset;
+      cfi->dw_cfi_oprnd1.dw_cfi_offset = const_offset;
     }
-
-#ifndef MIPS_DEBUGGING_INFO  /* SGI dbx thinks this means no offset.  */
-  else if (new_cfa->offset == old_cfa->offset
+  else if (new_cfa->offset.is_constant ()
+          && known_eq (new_cfa->offset, old_cfa->offset)
           && old_cfa->reg != INVALID_REGNUM
           && !new_cfa->indirect
           && !old_cfa->indirect)
     {
       /* Construct a "DW_CFA_def_cfa_register <register>" instruction,
         indicating the CFA register has changed to <register> but the
-        offset has not changed.  */
+        offset has not changed.  This requires the old CFA to have
+        been set as a register plus offset rather than a general
+        DW_CFA_def_cfa_expression.  */
       cfi->dw_cfi_opc = DW_CFA_def_cfa_register;
       cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
     }
-#endif
-
-  else if (new_cfa->indirect == 0)
+  else if (new_cfa->indirect == 0
+          && new_cfa->offset.is_constant (&const_offset))
     {
       /* Construct a "DW_CFA_def_cfa <register> <offset>" instruction,
         indicating the CFA register has changed to <register> with
         the specified offset.  The data factoring for DW_CFA_def_cfa_sf
         happens in output_cfi, or in the assembler via the .cfi_def_cfa
         directive.  */
-      if (new_cfa->offset < 0)
+      if (const_offset < 0)
        cfi->dw_cfi_opc = DW_CFA_def_cfa_sf;
       else
        cfi->dw_cfi_opc = DW_CFA_def_cfa;
       cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
-      cfi->dw_cfi_oprnd2.dw_cfi_offset = new_cfa->offset;
+      cfi->dw_cfi_oprnd2.dw_cfi_offset = const_offset;
     }
   else
     {
       /* Construct a DW_CFA_def_cfa_expression instruction to
         calculate the CFA using a full location expression since no
         register-offset pair is available.  */
-      struct dw_loc_descr_struct *loc_list;
+      struct dw_loc_descr_node *loc_list;
 
       cfi->dw_cfi_opc = DW_CFA_def_cfa_expression;
       loc_list = build_cfa_loc (new_cfa, 0);
       cfi->dw_cfi_oprnd1.dw_cfi_loc = loc_list;
+      if (!new_cfa->offset.is_constant ()
+         || !new_cfa->base_offset.is_constant ())
+       /* It's hard to reconstruct the CFA location for a polynomial
+          expression, so just cache it instead.  */
+       cfi->dw_cfi_oprnd2.dw_cfi_cfa_loc = copy_cfa (new_cfa);
+      else
+       cfi->dw_cfi_oprnd2.dw_cfi_cfa_loc = NULL;
     }
 
   return cfi;
@@ -790,33 +889,42 @@ def_cfa_1 (dw_cfa_location *new_cfa)
    otherwise it is saved in SREG.  */
 
 static void
-reg_save (unsigned int reg, unsigned int sreg, HOST_WIDE_INT offset)
+reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
 {
   dw_fde_ref fde = cfun ? cfun->fde : NULL;
   dw_cfi_ref cfi = new_cfi ();
 
   cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
 
-  /* When stack is aligned, store REG using DW_CFA_expression with FP.  */
-  if (fde
-      && fde->stack_realign
-      && sreg == INVALID_REGNUM)
+  if (sreg == INVALID_REGNUM)
     {
-      cfi->dw_cfi_opc = DW_CFA_expression;
-      cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
-      cfi->dw_cfi_oprnd2.dw_cfi_loc
-       = build_cfa_aligned_loc (&cur_row->cfa, offset,
-                                fde->stack_realignment);
-    }
-  else if (sreg == INVALID_REGNUM)
-    {
-      if (need_data_align_sf_opcode (offset))
-       cfi->dw_cfi_opc = DW_CFA_offset_extended_sf;
-      else if (reg & ~0x3f)
-       cfi->dw_cfi_opc = DW_CFA_offset_extended;
+      HOST_WIDE_INT const_offset;
+      /* When stack is aligned, store REG using DW_CFA_expression with FP.  */
+      if (fde && fde->stack_realign)
+       {
+         cfi->dw_cfi_opc = DW_CFA_expression;
+         cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
+         cfi->dw_cfi_oprnd2.dw_cfi_loc
+           = build_cfa_aligned_loc (&cur_row->cfa, offset,
+                                    fde->stack_realignment);
+       }
+      else if (offset.is_constant (&const_offset))
+       {
+         if (need_data_align_sf_opcode (const_offset))
+           cfi->dw_cfi_opc = DW_CFA_offset_extended_sf;
+         else if (reg & ~0x3f)
+           cfi->dw_cfi_opc = DW_CFA_offset_extended;
+         else
+           cfi->dw_cfi_opc = DW_CFA_offset;
+         cfi->dw_cfi_oprnd2.dw_cfi_offset = const_offset;
+       }
       else
-       cfi->dw_cfi_opc = DW_CFA_offset;
-      cfi->dw_cfi_oprnd2.dw_cfi_offset = offset;
+       {
+         cfi->dw_cfi_opc = DW_CFA_expression;
+         cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
+         cfi->dw_cfi_oprnd2.dw_cfi_loc
+           = build_cfa_loc (&cur_row->cfa, offset);
+       }
     }
   else if (sreg == reg)
     {
@@ -836,214 +944,67 @@ reg_save (unsigned int reg, unsigned int sreg, HOST_WIDE_INT offset)
   update_row_reg_save (cur_row, reg, cfi);
 }
 
-/* Given a SET, calculate the amount of stack adjustment it
-   contains.  */
+/* A subroutine of scan_trace.  Check INSN for a REG_ARGS_SIZE note
+   and adjust data structures to match.  */
 
-static HOST_WIDE_INT
-stack_adjust_offset (const_rtx pattern, HOST_WIDE_INT cur_args_size,
-                    HOST_WIDE_INT cur_offset)
+static void
+notice_args_size (rtx_insn *insn)
 {
-  const_rtx src = SET_SRC (pattern);
-  const_rtx dest = SET_DEST (pattern);
-  HOST_WIDE_INT offset = 0;
-  enum rtx_code code;
+  poly_int64 args_size, delta;
+  rtx note;
 
-  if (dest == stack_pointer_rtx)
-    {
-      code = GET_CODE (src);
+  note = find_reg_note (insn, REG_ARGS_SIZE, NULL);
+  if (note == NULL)
+    return;
 
-      /* Assume (set (reg sp) (reg whatever)) sets args_size
-        level to 0.  */
-      if (code == REG && src != stack_pointer_rtx)
-       {
-         offset = -cur_args_size;
-#ifndef STACK_GROWS_DOWNWARD
-         offset = -offset;
-#endif
-         return offset - cur_offset;
-       }
+  if (!cur_trace->eh_head)
+    cur_trace->args_size_defined_for_eh = true;
 
-      if (! (code == PLUS || code == MINUS)
-         || XEXP (src, 0) != stack_pointer_rtx
-         || !CONST_INT_P (XEXP (src, 1)))
-       return 0;
+  args_size = get_args_size (note);
+  delta = args_size - cur_trace->end_true_args_size;
+  if (known_eq (delta, 0))
+    return;
 
-      /* (set (reg sp) (plus (reg sp) (const_int))) */
-      offset = INTVAL (XEXP (src, 1));
-      if (code == PLUS)
-       offset = -offset;
-      return offset;
-    }
+  cur_trace->end_true_args_size = args_size;
 
-  if (MEM_P (src) && !MEM_P (dest))
-    dest = src;
-  if (MEM_P (dest))
+  /* If the CFA is computed off the stack pointer, then we must adjust
+     the computation of the CFA as well.  */
+  if (cur_cfa->reg == dw_stack_pointer_regnum)
     {
-      /* (set (mem (pre_dec (reg sp))) (foo)) */
-      src = XEXP (dest, 0);
-      code = GET_CODE (src);
-
-      switch (code)
-       {
-       case PRE_MODIFY:
-       case POST_MODIFY:
-         if (XEXP (src, 0) == stack_pointer_rtx)
-           {
-             rtx val = XEXP (XEXP (src, 1), 1);
-             /* We handle only adjustments by constant amount.  */
-             gcc_assert (GET_CODE (XEXP (src, 1)) == PLUS
-                         && CONST_INT_P (val));
-             offset = -INTVAL (val);
-             break;
-           }
-         return 0;
+      gcc_assert (!cur_cfa->indirect);
 
-       case PRE_DEC:
-       case POST_DEC:
-         if (XEXP (src, 0) == stack_pointer_rtx)
-           {
-             offset = GET_MODE_SIZE (GET_MODE (dest));
-             break;
-           }
-         return 0;
-
-       case PRE_INC:
-       case POST_INC:
-         if (XEXP (src, 0) == stack_pointer_rtx)
-           {
-             offset = -GET_MODE_SIZE (GET_MODE (dest));
-             break;
-           }
-         return 0;
+      /* Convert a change in args_size (always a positive in the
+        direction of stack growth) to a change in stack pointer.  */
+      if (!STACK_GROWS_DOWNWARD)
+       delta = -delta;
 
-       default:
-         return 0;
-       }
+      cur_cfa->offset += delta;
     }
-  else
-    return 0;
-
-  return offset;
-}
-
-/* Add a CFI to update the running total of the size of arguments
-   pushed onto the stack.  */
-
-static void
-dwarf2out_args_size (HOST_WIDE_INT size)
-{
-  if (size == cur_row->args_size)
-    return;
-
-  cur_row->args_size = size;
-  add_cfi_args_size (size);
-}
-
-/* Record a stack adjustment of OFFSET bytes.  */
-
-static void
-dwarf2out_stack_adjust (HOST_WIDE_INT offset)
-{
-  dw_cfa_location loc = cur_row->cfa;
-
-  if (loc.reg == dw_stack_pointer_regnum)
-    loc.offset += offset;
-
-  if (cur_trace->cfa_store.reg == dw_stack_pointer_regnum)
-    cur_trace->cfa_store.offset += offset;
-
-  /* ??? The assumption seems to be that if A_O_A, the only CFA adjustments
-     involving the stack pointer are inside the prologue and marked as
-     RTX_FRAME_RELATED_P.  That said, should we not verify this assumption
-     by *asserting* A_O_A at this point?  Why else would we have a change
-     to the stack pointer?  */
-  if (ACCUMULATE_OUTGOING_ARGS)
-    return;
-
-#ifndef STACK_GROWS_DOWNWARD
-  offset = -offset;
-#endif
-
-  queued_args_size += offset;
-  if (queued_args_size < 0)
-    queued_args_size = 0;
-
-  def_cfa_1 (&loc);
-  if (flag_asynchronous_unwind_tables)
-    dwarf2out_args_size (queued_args_size);
 }
 
-/* Check INSN to see if it looks like a push or a stack adjustment, and
-   make a note of it if it does.  EH uses this information to find out
-   how much extra space it needs to pop off the stack.  */
+/* A subroutine of scan_trace.  INSN is can_throw_internal.  Update the
+   data within the trace related to EH insns and args_size.  */
 
 static void
-dwarf2out_notice_stack_adjust (rtx insn, bool after_p)
+notice_eh_throw (rtx_insn *insn)
 {
-  HOST_WIDE_INT offset;
-  int i;
-
-  /* Don't handle epilogues at all.  Certainly it would be wrong to do so
-     with this function.  Proper support would require all frame-related
-     insns to be marked, and to be able to handle saving state around
-     epilogues textually in the middle of the function.  */
-  if (prologue_epilogue_contains (insn))
-    return;
-
-  /* If INSN is an instruction from target of an annulled branch, the
-     effects are for the target only and so current argument size
-     shouldn't change at all.  */
-  if (final_sequence
-      && INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0))
-      && INSN_FROM_TARGET_P (insn))
-    return;
-
-  /* If only calls can throw, and we have a frame pointer,
-     save up adjustments until we see the CALL_INSN.  */
-  if (!flag_asynchronous_unwind_tables
-      && cur_row->cfa.reg != dw_stack_pointer_regnum)
+  poly_int64 args_size = cur_trace->end_true_args_size;
+  if (cur_trace->eh_head == NULL)
     {
-      if (CALL_P (insn) && !after_p)
-       {
-         /* Extract the size of the args from the CALL rtx itself.  */
-         insn = PATTERN (insn);
-         if (GET_CODE (insn) == PARALLEL)
-           insn = XVECEXP (insn, 0, 0);
-         if (GET_CODE (insn) == SET)
-           insn = SET_SRC (insn);
-         gcc_assert (GET_CODE (insn) == CALL);
-         dwarf2out_args_size (INTVAL (XEXP (insn, 1)));
-       }
-      return;
+      cur_trace->eh_head = insn;
+      cur_trace->beg_delay_args_size = args_size;
+      cur_trace->end_delay_args_size = args_size;
     }
-
-  if (CALL_P (insn) && !after_p)
+  else if (maybe_ne (cur_trace->end_delay_args_size, args_size))
     {
-      if (!flag_asynchronous_unwind_tables)
-       dwarf2out_args_size (queued_args_size);
-      return;
-    }
-  else if (BARRIER_P (insn))
-    return;
-  else if (GET_CODE (PATTERN (insn)) == SET)
-    offset = stack_adjust_offset (PATTERN (insn), queued_args_size, 0);
-  else if (GET_CODE (PATTERN (insn)) == PARALLEL
-          || GET_CODE (PATTERN (insn)) == SEQUENCE)
-    {
-      /* There may be stack adjustments inside compound insns.  Search
-        for them.  */
-      for (offset = 0, i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
-       if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET)
-         offset += stack_adjust_offset (XVECEXP (PATTERN (insn), 0, i),
-                                        queued_args_size, offset);
-    }
-  else
-    return;
-
-  if (offset == 0)
-    return;
+      cur_trace->end_delay_args_size = args_size;
 
-  dwarf2out_stack_adjust (offset);
+      /* ??? If the CFA is the stack pointer, search backward for the last
+        CFI note and insert there.  Given that the stack changed for the
+        args_size change, there *must* be such a note in between here and
+        the last eh insn.  */
+      add_cfi_args_size (args_size);
+    }
 }
 
 /* Short-hand inline for the very common D_F_R (REGNO (x)) operation.  */
@@ -1053,6 +1014,7 @@ dwarf2out_notice_stack_adjust (rtx insn, bool after_p)
 static inline unsigned
 dwf_regno (const_rtx reg)
 {
+  gcc_assert (REGNO (reg) < FIRST_PSEUDO_REGISTER);
   return DWARF_FRAME_REGNUM (REGNO (reg));
 }
 
@@ -1075,12 +1037,11 @@ record_reg_saved_in_reg (rtx dest, rtx src)
   reg_saved_in_data *elt;
   size_t i;
 
-  FOR_EACH_VEC_ELT (reg_saved_in_data, cur_trace->regs_saved_in_regs, i, elt)
+  FOR_EACH_VEC_ELT (cur_trace->regs_saved_in_regs, i, elt)
     if (compare_reg_or_pc (elt->orig_reg, src))
       {
        if (dest == NULL)
-         VEC_unordered_remove (reg_saved_in_data,
-                               cur_trace->regs_saved_in_regs, i);
+         cur_trace->regs_saved_in_regs.unordered_remove (i);
        else
          elt->saved_in_reg = dest;
        return;
@@ -1089,33 +1050,30 @@ record_reg_saved_in_reg (rtx dest, rtx src)
   if (dest == NULL)
     return;
 
-  elt = VEC_safe_push (reg_saved_in_data, heap,
-                      cur_trace->regs_saved_in_regs, NULL);
-  elt->orig_reg = src;
-  elt->saved_in_reg = dest;
+  reg_saved_in_data e = {src, dest};
+  cur_trace->regs_saved_in_regs.safe_push (e);
 }
 
 /* Add an entry to QUEUED_REG_SAVES saying that REG is now saved at
    SREG, or if SREG is NULL then it is saved at OFFSET to the CFA.  */
 
 static void
-queue_reg_save (rtx reg, rtx sreg, HOST_WIDE_INT offset)
+queue_reg_save (rtx reg, rtx sreg, poly_int64 offset)
 {
   queued_reg_save *q;
+  queued_reg_save e = {reg, sreg, offset};
   size_t i;
 
   /* Duplicates waste space, but it's also necessary to remove them
      for correctness, since the queue gets output in reverse order.  */
-  FOR_EACH_VEC_ELT (queued_reg_save, queued_reg_saves, i, q)
+  FOR_EACH_VEC_ELT (queued_reg_saves, i, q)
     if (compare_reg_or_pc (q->reg, reg))
-      goto found;
-
-  q = VEC_safe_push (queued_reg_save, heap, queued_reg_saves, NULL);
+      {
+       *q = e;
+       return;
+      }
 
- found:
-  q->reg = reg;
-  q->saved_reg = sreg;
-  q->cfa_offset = offset;
+  queued_reg_saves.safe_push (e);
 }
 
 /* Output all the entries in QUEUED_REG_SAVES.  */
@@ -1126,7 +1084,7 @@ dwarf2out_flush_queued_reg_saves (void)
   queued_reg_save *q;
   size_t i;
 
-  FOR_EACH_VEC_ELT (queued_reg_save, queued_reg_saves, i, q)
+  FOR_EACH_VEC_ELT (queued_reg_saves, i, q)
     {
       unsigned int reg, sreg;
 
@@ -1143,7 +1101,7 @@ dwarf2out_flush_queued_reg_saves (void)
       reg_save (reg, sreg, q->cfa_offset);
     }
 
-  VEC_truncate (queued_reg_save, queued_reg_saves, 0);
+  queued_reg_saves.truncate (0);
 }
 
 /* Does INSN clobber any register which QUEUED_REG_SAVES lists a saved
@@ -1157,7 +1115,7 @@ clobbers_queued_reg_save (const_rtx insn)
   queued_reg_save *q;
   size_t iq;
 
-  FOR_EACH_VEC_ELT (queued_reg_save, queued_reg_saves, iq, q)
+  FOR_EACH_VEC_ELT (queued_reg_saves, iq, q)
     {
       size_t ir;
       reg_saved_in_data *rir;
@@ -1165,8 +1123,7 @@ clobbers_queued_reg_save (const_rtx insn)
       if (modified_in_p (q->reg, insn))
        return true;
 
-      FOR_EACH_VEC_ELT (reg_saved_in_data,
-                       cur_trace->regs_saved_in_regs, ir, rir)
+      FOR_EACH_VEC_ELT (cur_trace->regs_saved_in_regs, ir, rir)
        if (compare_reg_or_pc (q->reg, rir->orig_reg)
            && modified_in_p (rir->saved_in_reg, insn))
          return true;
@@ -1185,11 +1142,11 @@ reg_saved_in (rtx reg)
   reg_saved_in_data *rir;
   size_t i;
 
-  FOR_EACH_VEC_ELT (queued_reg_save, queued_reg_saves, i, q)
+  FOR_EACH_VEC_ELT (queued_reg_saves, i, q)
     if (q->saved_reg && regn == REGNO (q->saved_reg))
       return q->reg;
 
-  FOR_EACH_VEC_ELT (reg_saved_in_data, cur_trace->regs_saved_in_regs, i, rir)
+  FOR_EACH_VEC_ELT (cur_trace->regs_saved_in_regs, i, rir)
     if (regn == REGNO (rir->saved_in_reg))
       return rir->orig_reg;
 
@@ -1201,31 +1158,18 @@ reg_saved_in (rtx reg)
 static void
 dwarf2out_frame_debug_def_cfa (rtx pat)
 {
-  dw_cfa_location loc;
+  memset (cur_cfa, 0, sizeof (*cur_cfa));
 
-  memset (&loc, 0, sizeof (loc));
-
-  if (GET_CODE (pat) == PLUS)
-    {
-      loc.offset = INTVAL (XEXP (pat, 1));
-      pat = XEXP (pat, 0);
-    }
+  pat = strip_offset (pat, &cur_cfa->offset);
   if (MEM_P (pat))
     {
-      loc.indirect = 1;
-      pat = XEXP (pat, 0);
-      if (GET_CODE (pat) == PLUS)
-       {
-         loc.base_offset = INTVAL (XEXP (pat, 1));
-         pat = XEXP (pat, 0);
-       }
+      cur_cfa->indirect = 1;
+      pat = strip_offset (XEXP (pat, 0), &cur_cfa->base_offset);
     }
   /* ??? If this fails, we could be calling into the _loc functions to
      define a full expression.  So far no port does that.  */
   gcc_assert (REG_P (pat));
-  loc.reg = dwf_regno (pat);
-
-  def_cfa_1 (&loc);
+  cur_cfa->reg = dwf_regno (pat);
 }
 
 /* A subroutine of dwarf2out_frame_debug, process a REG_ADJUST_CFA note.  */
@@ -1233,7 +1177,6 @@ dwarf2out_frame_debug_def_cfa (rtx pat)
 static void
 dwarf2out_frame_debug_adjust_cfa (rtx pat)
 {
-  dw_cfa_location loc = cur_row->cfa;
   rtx src, dest;
 
   gcc_assert (GET_CODE (pat) == SET);
@@ -1243,21 +1186,19 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
   switch (GET_CODE (src))
     {
     case PLUS:
-      gcc_assert (dwf_regno (XEXP (src, 0)) == loc.reg);
-      loc.offset -= INTVAL (XEXP (src, 1));
+      gcc_assert (dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
+      cur_cfa->offset -= rtx_to_poly_int64 (XEXP (src, 1));
       break;
 
     case REG:
-       break;
+      break;
 
     default:
-       gcc_unreachable ();
+      gcc_unreachable ();
     }
 
-  loc.reg = dwf_regno (dest);
-  gcc_assert (loc.indirect == 0);
-
-  def_cfa_1 (&loc);
+  cur_cfa->reg = dwf_regno (dest);
+  gcc_assert (cur_cfa->indirect == 0);
 }
 
 /* A subroutine of dwarf2out_frame_debug, process a REG_CFA_OFFSET note.  */
@@ -1265,7 +1206,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
 static void
 dwarf2out_frame_debug_cfa_offset (rtx set)
 {
-  HOST_WIDE_INT offset;
+  poly_int64 offset;
   rtx src, addr, span;
   unsigned int sregno;
 
@@ -1278,12 +1219,12 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
   switch (GET_CODE (addr))
     {
     case REG:
-      gcc_assert (dwf_regno (addr) == cur_row->cfa.reg);
-      offset = -cur_row->cfa.offset;
+      gcc_assert (dwf_regno (addr) == cur_cfa->reg);
+      offset = -cur_cfa->offset;
       break;
     case PLUS:
-      gcc_assert (dwf_regno (XEXP (addr, 0)) == cur_row->cfa.reg);
-      offset = INTVAL (XEXP (addr, 1)) - cur_row->cfa.offset;
+      gcc_assert (dwf_regno (XEXP (addr, 0)) == cur_cfa->reg);
+      offset = rtx_to_poly_int64 (XEXP (addr, 1)) - cur_cfa->offset;
       break;
     default:
       gcc_unreachable ();
@@ -1307,18 +1248,15 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
   else
     {
       /* We have a PARALLEL describing where the contents of SRC live.
-        Queue register saves for each piece of the PARALLEL.  */
-      int par_index;
-      int limit;
-      HOST_WIDE_INT span_offset = offset;
+        Adjust the offset for each piece of the PARALLEL.  */
+      poly_int64 span_offset = offset;
 
       gcc_assert (GET_CODE (span) == PARALLEL);
 
-      limit = XVECLEN (span, 0);
-      for (par_index = 0; par_index < limit; par_index++)
+      const int par_len = XVECLEN (span, 0);
+      for (int par_index = 0; par_index < par_len; par_index++)
        {
          rtx elem = XVECEXP (span, 0, par_index);
-
          sregno = dwf_regno (src);
          reg_save (sregno, INVALID_REGNUM, span_offset);
          span_offset += GET_MODE_SIZE (GET_MODE (elem));
@@ -1350,7 +1288,7 @@ dwarf2out_frame_debug_cfa_register (rtx set)
   reg_save (sregno, dregno, 0);
 }
 
-/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_EXPRESSION note. */
+/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_EXPRESSION note.  */
 
 static void
 dwarf2out_frame_debug_cfa_expression (rtx set)
@@ -1382,20 +1320,65 @@ dwarf2out_frame_debug_cfa_expression (rtx set)
   update_row_reg_save (cur_row, regno, cfi);
 }
 
+/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_VAL_EXPRESSION
+   note.  */
+
+static void
+dwarf2out_frame_debug_cfa_val_expression (rtx set)
+{
+  rtx dest = SET_DEST (set);
+  gcc_assert (REG_P (dest));
+
+  rtx span = targetm.dwarf_register_span (dest);
+  gcc_assert (!span);
+
+  rtx src = SET_SRC (set);
+  dw_cfi_ref cfi = new_cfi ();
+  cfi->dw_cfi_opc = DW_CFA_val_expression;
+  cfi->dw_cfi_oprnd1.dw_cfi_reg_num = dwf_regno (dest);
+  cfi->dw_cfi_oprnd2.dw_cfi_loc
+    = mem_loc_descriptor (src, GET_MODE (src),
+                         GET_MODE (dest), VAR_INIT_STATUS_INITIALIZED);
+  add_cfi (cfi);
+  update_row_reg_save (cur_row, dwf_regno (dest), cfi);
+}
+
 /* A subroutine of dwarf2out_frame_debug, process a REG_CFA_RESTORE note.  */
 
 static void
 dwarf2out_frame_debug_cfa_restore (rtx reg)
 {
-  unsigned int regno = dwf_regno (reg);
+  gcc_assert (REG_P (reg));
+
+  rtx span = targetm.dwarf_register_span (reg);
+  if (!span)
+    {
+      unsigned int regno = dwf_regno (reg);
+      add_cfi_restore (regno);
+      update_row_reg_save (cur_row, regno, NULL);
+    }
+  else
+    {
+      /* We have a PARALLEL describing where the contents of REG live.
+        Restore the register for each piece of the PARALLEL.  */
+      gcc_assert (GET_CODE (span) == PARALLEL);
 
-  add_cfi_restore (regno);
-  update_row_reg_save (cur_row, regno, NULL);
+      const int par_len = XVECLEN (span, 0);
+      for (int par_index = 0; par_index < par_len; par_index++)
+       {
+         reg = XVECEXP (span, 0, par_index);
+         gcc_assert (REG_P (reg));
+         unsigned int regno = dwf_regno (reg);
+         add_cfi_restore (regno);
+         update_row_reg_save (cur_row, regno, NULL);
+       }
+    }
 }
 
 /* A subroutine of dwarf2out_frame_debug, process a REG_CFA_WINDOW_SAVE.
-   ??? Perhaps we should note in the CIE where windows are saved (instead of
-   assuming 0(cfa)) and what registers are in the window.  */
+
+   ??? Perhaps we should note in the CIE where windows are saved (instead
+   of assuming 0(cfa)) and what registers are in the window.  */
 
 static void
 dwarf2out_frame_debug_cfa_window_save (void)
@@ -1404,6 +1387,22 @@ dwarf2out_frame_debug_cfa_window_save (void)
 
   cfi->dw_cfi_opc = DW_CFA_GNU_window_save;
   add_cfi (cfi);
+  cur_row->window_save = true;
+}
+
+/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_TOGGLE_RA_MANGLE.
+   Note: DW_CFA_GNU_window_save dwarf opcode is reused for toggling RA mangle
+   state, this is a target specific operation on AArch64 and can only be used
+   on other targets if they don't use the window save operation otherwise.  */
+
+static void
+dwarf2out_frame_debug_cfa_toggle_ra_mangle (void)
+{
+  dw_cfi_ref cfi = new_cfi ();
+
+  cfi->dw_cfi_opc = DW_CFA_GNU_window_save;
+  add_cfi (cfi);
+  cur_row->ra_mangled = !cur_row->ra_mangled;
 }
 
 /* Record call frame debugging information for an expression EXPR,
@@ -1451,7 +1450,7 @@ dwarf2out_frame_debug_cfa_window_save (void)
 
   cfa         current rule for calculating the CFA.  It usually
               consists of a register and an offset.  This is
-              actually stored in cur_row->cfa, but abbreviated
+              actually stored in *cur_cfa, but abbreviated
               for the purposes of this documentation.
   cfa_store    register used by prologue code to save things to the stack
               cfa_store.offset is the offset from the value of
@@ -1606,9 +1605,8 @@ dwarf2out_frame_debug_cfa_window_save (void)
 static void
 dwarf2out_frame_debug_expr (rtx expr)
 {
-  dw_cfa_location cfa = cur_row->cfa;
   rtx src, dest, span;
-  HOST_WIDE_INT offset;
+  poly_int64 offset;
   dw_fde_ref fde;
 
   /* If RTX_FRAME_RELATED_P is set on a PARALLEL, process each member of
@@ -1644,18 +1642,6 @@ dwarf2out_frame_debug_expr (rtx expr)
              && (!MEM_P (SET_DEST (elem)) || GET_CODE (expr) == SEQUENCE)
              && (RTX_FRAME_RELATED_P (elem) || par_index == 0))
            dwarf2out_frame_debug_expr (elem);
-         else if (GET_CODE (elem) == SET
-                  && par_index != 0
-                  && !RTX_FRAME_RELATED_P (elem))
-           {
-             /* Stack adjustment combining might combine some post-prologue
-                stack adjustment into a prologue stack adjustment.  */
-             HOST_WIDE_INT offset
-               = stack_adjust_offset (elem, queued_args_size, 0);
-
-             if (offset != 0)
-               dwarf2out_stack_adjust (offset);
-           }
        }
       return;
     }
@@ -1681,7 +1667,7 @@ dwarf2out_frame_debug_expr (rtx expr)
        {
          /* Setting FP from SP.  */
        case REG:
-         if (cfa.reg == dwf_regno (src))
+         if (cur_cfa->reg == dwf_regno (src))
            {
              /* Rule 1 */
              /* Update the CFA rule wrt SP or FP.  Make sure src is
@@ -1691,9 +1677,9 @@ dwarf2out_frame_debug_expr (rtx expr)
                 ARM copies SP to a temporary register, and from there to
                 FP.  So we just rely on the backends to only set
                 RTX_FRAME_RELATED_P on appropriate insns.  */
-             cfa.reg = dwf_regno (dest);
-             cur_trace->cfa_temp.reg = cfa.reg;
-             cur_trace->cfa_temp.offset = cfa.offset;
+             cur_cfa->reg = dwf_regno (dest);
+             cur_trace->cfa_temp.reg = cur_cfa->reg;
+             cur_trace->cfa_temp.offset = cur_cfa->offset;
            }
          else
            {
@@ -1711,7 +1697,7 @@ dwarf2out_frame_debug_expr (rtx expr)
                  && REGNO (src) == STACK_POINTER_REGNUM)
                gcc_assert (REGNO (dest) == HARD_FRAME_POINTER_REGNUM
                            && fde->drap_reg != INVALID_REGNUM
-                           && cfa.reg != dwf_regno (src));
+                           && cur_cfa->reg != dwf_regno (src));
              else
                queue_reg_save (src, dest, 0);
            }
@@ -1724,25 +1710,20 @@ dwarf2out_frame_debug_expr (rtx expr)
            {
              /* Rule 2 */
              /* Adjusting SP.  */
-             switch (GET_CODE (XEXP (src, 1)))
+             if (REG_P (XEXP (src, 1)))
                {
-               case CONST_INT:
-                 offset = INTVAL (XEXP (src, 1));
-                 break;
-               case REG:
                  gcc_assert (dwf_regno (XEXP (src, 1))
                              == cur_trace->cfa_temp.reg);
                  offset = cur_trace->cfa_temp.offset;
-                 break;
-               default:
-                 gcc_unreachable ();
                }
+             else if (!poly_int_rtx_p (XEXP (src, 1), &offset))
+               gcc_unreachable ();
 
              if (XEXP (src, 0) == hard_frame_pointer_rtx)
                {
                  /* Restoring SP from FP in the epilogue.  */
-                 gcc_assert (cfa.reg == dw_frame_pointer_regnum);
-                 cfa.reg = dw_stack_pointer_regnum;
+                 gcc_assert (cur_cfa->reg == dw_frame_pointer_regnum);
+                 cur_cfa->reg = dw_stack_pointer_regnum;
                }
              else if (GET_CODE (src) == LO_SUM)
                /* Assume we've set the source reg of the LO_SUM from sp.  */
@@ -1752,8 +1733,8 @@ dwarf2out_frame_debug_expr (rtx expr)
 
              if (GET_CODE (src) != MINUS)
                offset = -offset;
-             if (cfa.reg == dw_stack_pointer_regnum)
-               cfa.offset += offset;
+             if (cur_cfa->reg == dw_stack_pointer_regnum)
+               cur_cfa->offset += offset;
              if (cur_trace->cfa_store.reg == dw_stack_pointer_regnum)
                cur_trace->cfa_store.offset += offset;
            }
@@ -1765,13 +1746,12 @@ dwarf2out_frame_debug_expr (rtx expr)
              gcc_assert (frame_pointer_needed);
 
              gcc_assert (REG_P (XEXP (src, 0))
-                         && dwf_regno (XEXP (src, 0)) == cfa.reg
-                         && CONST_INT_P (XEXP (src, 1)));
-             offset = INTVAL (XEXP (src, 1));
+                         && dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
+             offset = rtx_to_poly_int64 (XEXP (src, 1));
              if (GET_CODE (src) != MINUS)
                offset = -offset;
-             cfa.offset += offset;
-             cfa.reg = dw_frame_pointer_regnum;
+             cur_cfa->offset += offset;
+             cur_cfa->reg = dw_frame_pointer_regnum;
            }
          else
            {
@@ -1779,17 +1759,17 @@ dwarf2out_frame_debug_expr (rtx expr)
 
              /* Rule 4 */
              if (REG_P (XEXP (src, 0))
-                 && dwf_regno (XEXP (src, 0)) == cfa.reg
-                 && CONST_INT_P (XEXP (src, 1)))
+                 && dwf_regno (XEXP (src, 0)) == cur_cfa->reg
+                 && poly_int_rtx_p (XEXP (src, 1), &offset))
                {
                  /* Setting a temporary CFA register that will be copied
                     into the FP later on.  */
-                 offset = - INTVAL (XEXP (src, 1));
-                 cfa.offset += offset;
-                 cfa.reg = dwf_regno (dest);
+                 offset = -offset;
+                 cur_cfa->offset += offset;
+                 cur_cfa->reg = dwf_regno (dest);
                  /* Or used to save regs to the stack.  */
-                 cur_trace->cfa_temp.reg = cfa.reg;
-                 cur_trace->cfa_temp.offset = cfa.offset;
+                 cur_trace->cfa_temp.reg = cur_cfa->reg;
+                 cur_trace->cfa_temp.offset = cur_cfa->offset;
                }
 
              /* Rule 5 */
@@ -1799,19 +1779,17 @@ dwarf2out_frame_debug_expr (rtx expr)
                {
                  /* Setting a scratch register that we will use instead
                     of SP for saving registers to the stack.  */
-                 gcc_assert (cfa.reg == dw_stack_pointer_regnum);
+                 gcc_assert (cur_cfa->reg == dw_stack_pointer_regnum);
                  cur_trace->cfa_store.reg = dwf_regno (dest);
                  cur_trace->cfa_store.offset
-                   = cfa.offset - cur_trace->cfa_temp.offset;
+                   = cur_cfa->offset - cur_trace->cfa_temp.offset;
                }
 
              /* Rule 9 */
              else if (GET_CODE (src) == LO_SUM
-                      && CONST_INT_P (XEXP (src, 1)))
-               {
-                 cur_trace->cfa_temp.reg = dwf_regno (dest);
-                 cur_trace->cfa_temp.offset = INTVAL (XEXP (src, 1));
-               }
+                      && poly_int_rtx_p (XEXP (src, 1),
+                                         &cur_trace->cfa_temp.offset))
+               cur_trace->cfa_temp.reg = dwf_regno (dest);
              else
                gcc_unreachable ();
            }
@@ -1819,8 +1797,9 @@ dwarf2out_frame_debug_expr (rtx expr)
 
          /* Rule 6 */
        case CONST_INT:
+       case CONST_POLY_INT:
          cur_trace->cfa_temp.reg = dwf_regno (dest);
-         cur_trace->cfa_temp.offset = INTVAL (src);
+         cur_trace->cfa_temp.offset = rtx_to_poly_int64 (src);
          break;
 
          /* Rule 7 */
@@ -1830,7 +1809,11 @@ dwarf2out_frame_debug_expr (rtx expr)
                      && CONST_INT_P (XEXP (src, 1)));
 
          cur_trace->cfa_temp.reg = dwf_regno (dest);
-         cur_trace->cfa_temp.offset |= INTVAL (XEXP (src, 1));
+         if (!can_ior_p (cur_trace->cfa_temp.offset, INTVAL (XEXP (src, 1)),
+                         &cur_trace->cfa_temp.offset))
+           /* The target shouldn't generate this kind of CFI note if we
+              can't represent it.  */
+           gcc_unreachable ();
          break;
 
          /* Skip over HIGH, assuming it will be followed by a LO_SUM,
@@ -1863,17 +1846,15 @@ dwarf2out_frame_debug_expr (rtx expr)
               fde->stack_realignment = INTVAL (XEXP (src, 1));
               cur_trace->cfa_store.offset = 0;
 
-             if (cfa.reg != dw_stack_pointer_regnum
-                 && cfa.reg != dw_frame_pointer_regnum)
-               fde->drap_reg = cfa.reg;
+             if (cur_cfa->reg != dw_stack_pointer_regnum
+                 && cur_cfa->reg != dw_frame_pointer_regnum)
+               fde->drap_reg = cur_cfa->reg;
             }
           return;
 
        default:
          gcc_unreachable ();
        }
-
-      def_cfa_1 (&cfa);
       break;
 
     case MEM:
@@ -1887,16 +1868,14 @@ dwarf2out_frame_debug_expr (rtx expr)
        case PRE_MODIFY:
        case POST_MODIFY:
          /* We can't handle variable size modifications.  */
-         gcc_assert (GET_CODE (XEXP (XEXP (XEXP (dest, 0), 1), 1))
-                     == CONST_INT);
-         offset = -INTVAL (XEXP (XEXP (XEXP (dest, 0), 1), 1));
+         offset = -rtx_to_poly_int64 (XEXP (XEXP (XEXP (dest, 0), 1), 1));
 
          gcc_assert (REGNO (XEXP (XEXP (dest, 0), 0)) == STACK_POINTER_REGNUM
                      && cur_trace->cfa_store.reg == dw_stack_pointer_regnum);
 
          cur_trace->cfa_store.offset += offset;
-         if (cfa.reg == dw_stack_pointer_regnum)
-           cfa.offset = cur_trace->cfa_store.offset;
+         if (cur_cfa->reg == dw_stack_pointer_regnum)
+           cur_cfa->offset = cur_trace->cfa_store.offset;
 
          if (GET_CODE (XEXP (dest, 0)) == POST_MODIFY)
            offset -= cur_trace->cfa_store.offset;
@@ -1923,14 +1902,15 @@ dwarf2out_frame_debug_expr (rtx expr)
             regiser.  */
           if (fde
               && fde->stack_realign
-              && src == hard_frame_pointer_rtx)
+             && REG_P (src)
+             && REGNO (src) == HARD_FRAME_POINTER_REGNUM)
            {
-             gcc_assert (cfa.reg != dw_frame_pointer_regnum);
+             gcc_assert (cur_cfa->reg != dw_frame_pointer_regnum);
              cur_trace->cfa_store.offset = 0;
            }
 
-         if (cfa.reg == dw_stack_pointer_regnum)
-           cfa.offset = cur_trace->cfa_store.offset;
+         if (cur_cfa->reg == dw_stack_pointer_regnum)
+           cur_cfa->offset = cur_trace->cfa_store.offset;
 
          if (GET_CODE (XEXP (dest, 0)) == POST_DEC)
            offset += -cur_trace->cfa_store.offset;
@@ -1946,16 +1926,15 @@ dwarf2out_frame_debug_expr (rtx expr)
          {
            unsigned int regno;
 
-           gcc_assert (CONST_INT_P (XEXP (XEXP (dest, 0), 1))
-                       && REG_P (XEXP (XEXP (dest, 0), 0)));
-           offset = INTVAL (XEXP (XEXP (dest, 0), 1));
+           gcc_assert (REG_P (XEXP (XEXP (dest, 0), 0)));
+           offset = rtx_to_poly_int64 (XEXP (XEXP (dest, 0), 1));
            if (GET_CODE (XEXP (dest, 0)) == MINUS)
              offset = -offset;
 
            regno = dwf_regno (XEXP (XEXP (dest, 0), 0));
 
-           if (cfa.reg == regno)
-             offset -= cfa.offset;
+           if (cur_cfa->reg == regno)
+             offset -= cur_cfa->offset;
            else if (cur_trace->cfa_store.reg == regno)
              offset -= cur_trace->cfa_store.offset;
            else
@@ -1972,8 +1951,8 @@ dwarf2out_frame_debug_expr (rtx expr)
          {
            unsigned int regno = dwf_regno (XEXP (dest, 0));
 
-           if (cfa.reg == regno)
-             offset = -cfa.offset;
+           if (cur_cfa->reg == regno)
+             offset = -cur_cfa->offset;
            else if (cur_trace->cfa_store.reg == regno)
              offset = -cur_trace->cfa_store.offset;
            else
@@ -2005,11 +1984,11 @@ dwarf2out_frame_debug_expr (rtx expr)
       if (REG_P (src)
          && REGNO (src) != STACK_POINTER_REGNUM
          && REGNO (src) != HARD_FRAME_POINTER_REGNUM
-         && dwf_regno (src) == cfa.reg)
+         && dwf_regno (src) == cur_cfa->reg)
        {
          /* We're storing the current CFA reg into the stack.  */
 
-         if (cfa.offset == 0)
+         if (known_eq (cur_cfa->offset, 0))
            {
               /* Rule 19 */
               /* If stack is aligned, putting CFA reg into stack means
@@ -2019,28 +1998,23 @@ dwarf2out_frame_debug_expr (rtx expr)
                 value.  */
               if (fde
                   && fde->stack_realign
-                  && cfa.indirect == 0
-                  && cfa.reg != dw_frame_pointer_regnum)
+                  && cur_cfa->indirect == 0
+                  && cur_cfa->reg != dw_frame_pointer_regnum)
                 {
-                 dw_cfa_location cfa_exp;
+                 gcc_assert (fde->drap_reg == cur_cfa->reg);
 
-                 gcc_assert (fde->drap_reg == cfa.reg);
-
-                 cfa_exp.indirect = 1;
-                 cfa_exp.reg = dw_frame_pointer_regnum;
-                 cfa_exp.base_offset = offset;
-                 cfa_exp.offset = 0;
+                 cur_cfa->indirect = 1;
+                 cur_cfa->reg = dw_frame_pointer_regnum;
+                 cur_cfa->base_offset = offset;
+                 cur_cfa->offset = 0;
 
                  fde->drap_reg_saved = 1;
-
-                 def_cfa_1 (&cfa_exp);
                  break;
                 }
 
              /* If the source register is exactly the CFA, assume
                 we're saving SP like any other register; this happens
                 on the ARM.  */
-             def_cfa_1 (&cfa);
              queue_reg_save (stack_pointer_rtx, NULL_RTX, offset);
              break;
            }
@@ -2054,33 +2028,30 @@ dwarf2out_frame_debug_expr (rtx expr)
                x = XEXP (x, 0);
              gcc_assert (REG_P (x));
 
-             cfa.reg = dwf_regno (x);
-             cfa.base_offset = offset;
-             cfa.indirect = 1;
-             def_cfa_1 (&cfa);
+             cur_cfa->reg = dwf_regno (x);
+             cur_cfa->base_offset = offset;
+             cur_cfa->indirect = 1;
              break;
            }
        }
 
-      def_cfa_1 (&cfa);
-
-      span = NULL;
       if (REG_P (src))
        span = targetm.dwarf_register_span (src);
+      else
+       span = NULL;
+
       if (!span)
        queue_reg_save (src, NULL_RTX, offset);
       else
        {
          /* We have a PARALLEL describing where the contents of SRC live.
             Queue register saves for each piece of the PARALLEL.  */
-         int par_index;
-         int limit;
-         HOST_WIDE_INT span_offset = offset;
+         poly_int64 span_offset = offset;
 
          gcc_assert (GET_CODE (span) == PARALLEL);
 
-         limit = XVECLEN (span, 0);
-         for (par_index = 0; par_index < limit; par_index++)
+         const int par_len = XVECLEN (span, 0);
+         for (int par_index = 0; par_index < par_len; par_index++)
            {
              rtx elem = XVECEXP (span, 0, par_index);
              queue_reg_save (elem, NULL_RTX, span_offset);
@@ -2094,40 +2065,21 @@ dwarf2out_frame_debug_expr (rtx expr)
     }
 }
 
-/* Record call frame debugging information for INSN, which either
-   sets SP or FP (adjusting how we calculate the frame address) or saves a
-   register to the stack.  If INSN is NULL_RTX, initialize our state.
-
-   If AFTER_P is false, we're being called before the insn is emitted,
-   otherwise after.  Call instructions get invoked twice.  */
+/* Record call frame debugging information for INSN, which either sets
+   SP or FP (adjusting how we calculate the frame address) or saves a
+   register to the stack.  */
 
 static void
-dwarf2out_frame_debug (rtx insn, bool after_p)
+dwarf2out_frame_debug (rtx_insn *insn)
 {
-  rtx note, n;
+  rtx note, n, pat;
   bool handled_one = false;
-  bool need_flush = false;
-
-  if (!NONJUMP_INSN_P (insn) || clobbers_queued_reg_save (insn))
-    dwarf2out_flush_queued_reg_saves ();
-
-  if (!RTX_FRAME_RELATED_P (insn))
-    {
-      /* ??? This should be done unconditionally since stack adjustments
-        matter if the stack pointer is not the CFA register anymore but
-        is still used to save registers.  */
-      if (!ACCUMULATE_OUTGOING_ARGS)
-       dwarf2out_notice_stack_adjust (insn, after_p);
-      return;
-    }
-
-  any_cfis_emitted = false;
 
   for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
     switch (REG_NOTE_KIND (note))
       {
       case REG_FRAME_RELATED_EXPR:
-       insn = XEXP (note, 0);
+       pat = XEXP (note, 0);
        goto do_frame_expr;
 
       case REG_CFA_DEF_CFA:
@@ -2168,10 +2120,16 @@ dwarf2out_frame_debug (rtx insn, bool after_p)
        break;
 
       case REG_CFA_EXPRESSION:
+      case REG_CFA_VAL_EXPRESSION:
        n = XEXP (note, 0);
        if (n == NULL)
          n = single_set (insn);
-       dwarf2out_frame_debug_cfa_expression (n);
+
+       if (REG_NOTE_KIND (note) == REG_CFA_EXPRESSION)
+         dwarf2out_frame_debug_cfa_expression (n);
+       else
+         dwarf2out_frame_debug_cfa_val_expression (n);
+
        handled_one = true;
        break;
 
@@ -2203,14 +2161,18 @@ dwarf2out_frame_debug (rtx insn, bool after_p)
        handled_one = true;
        break;
 
+      case REG_CFA_TOGGLE_RA_MANGLE:
+       dwarf2out_frame_debug_cfa_toggle_ra_mangle ();
+       handled_one = true;
+       break;
+
       case REG_CFA_WINDOW_SAVE:
        dwarf2out_frame_debug_cfa_window_save ();
        handled_one = true;
        break;
 
       case REG_CFA_FLUSH_QUEUE:
-       /* The actual flush happens below.  */
-       need_flush = true;
+       /* The actual flush happens elsewhere.  */
        handled_one = true;
        break;
 
@@ -2218,27 +2180,18 @@ dwarf2out_frame_debug (rtx insn, bool after_p)
        break;
       }
 
-  if (handled_one)
-    {
-      /* Minimize the number of advances by emitting the entire queue
-        once anything is emitted.  */
-      need_flush |= any_cfis_emitted;
-    }
-  else
+  if (!handled_one)
     {
-      insn = PATTERN (insn);
+      pat = PATTERN (insn);
     do_frame_expr:
-      dwarf2out_frame_debug_expr (insn);
+      dwarf2out_frame_debug_expr (pat);
 
       /* Check again.  A parallel can save and update the same register.
          We could probably check just once, here, but this is safer than
          removing the check at the start of the function.  */
-      if (any_cfis_emitted || clobbers_queued_reg_save (insn))
-       need_flush = true;
+      if (clobbers_queued_reg_save (pat))
+       dwarf2out_flush_queued_reg_saves ();
     }
-
-  if (need_flush)
-    dwarf2out_flush_queued_reg_saves ();
 }
 
 /* Emit CFI info to change the state from OLD_ROW to NEW_ROW.  */
@@ -2258,11 +2211,8 @@ change_cfi_row (dw_cfi_row *old_row, dw_cfi_row *new_row)
        add_cfi (cfi);
     }
 
-  if (old_row->args_size != new_row->args_size)
-    add_cfi_args_size (new_row->args_size);
-
-  n_old = VEC_length (dw_cfi_ref, old_row->reg_save);
-  n_new = VEC_length (dw_cfi_ref, new_row->reg_save);
+  n_old = vec_safe_length (old_row->reg_save);
+  n_new = vec_safe_length (new_row->reg_save);
   n_max = MAX (n_old, n_new);
 
   for (i = 0; i < n_max; ++i)
@@ -2270,9 +2220,9 @@ change_cfi_row (dw_cfi_row *old_row, dw_cfi_row *new_row)
       dw_cfi_ref r_old = NULL, r_new = NULL;
 
       if (i < n_old)
-       r_old = VEC_index (dw_cfi_ref, old_row->reg_save, i);
+       r_old = (*old_row->reg_save)[i];
       if (i < n_new)
-       r_new = VEC_index (dw_cfi_ref, new_row->reg_save, i);
+       r_new = (*new_row->reg_save)[i];
 
       if (r_old == r_new)
        ;
@@ -2281,6 +2231,25 @@ change_cfi_row (dw_cfi_row *old_row, dw_cfi_row *new_row)
       else if (!cfi_equal_p (r_old, r_new))
         add_cfi (r_new);
     }
+
+  if (!old_row->window_save && new_row->window_save)
+    {
+      dw_cfi_ref cfi = new_cfi ();
+
+      gcc_assert (!old_row->ra_mangled && !new_row->ra_mangled);
+      cfi->dw_cfi_opc = DW_CFA_GNU_window_save;
+      add_cfi (cfi);
+    }
+
+  if (old_row->ra_mangled != new_row->ra_mangled)
+    {
+      dw_cfi_ref cfi = new_cfi ();
+
+      gcc_assert (!old_row->window_save && !new_row->window_save);
+      /* DW_CFA_GNU_window_save is reused for toggling RA mangle state.  */
+      cfi->dw_cfi_opc = DW_CFA_GNU_window_save;
+      add_cfi (cfi);
+    }
 }
 
 /* Examine CFI and return true if a cfi label and set_loc is needed
@@ -2326,73 +2295,67 @@ static void
 add_cfis_to_fde (void)
 {
   dw_fde_ref fde = cfun->fde;
-  rtx insn, next;
-  /* We always start with a function_begin label.  */
-  bool first = false;
+  rtx_insn *insn, *next;
 
   for (insn = get_insns (); insn; insn = next)
     {
       next = NEXT_INSN (insn);
 
       if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS)
-       {
-         fde->dw_fde_switch_cfi_index
-           = VEC_length (dw_cfi_ref, fde->dw_fde_cfi);
-         /* Don't attempt to advance_loc4 between labels
-            in different sections.  */
-         first = true;
-       }
+       fde->dw_fde_switch_cfi_index = vec_safe_length (fde->dw_fde_cfi);
 
       if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_CFI)
        {
          bool required = cfi_label_required_p (NOTE_CFI (insn));
-         while (next && NOTE_P (next) && NOTE_KIND (next) == NOTE_INSN_CFI)
-           {
-             required |= cfi_label_required_p (NOTE_CFI (next));
+         while (next)
+           if (NOTE_P (next) && NOTE_KIND (next) == NOTE_INSN_CFI)
+             {
+               required |= cfi_label_required_p (NOTE_CFI (next));
+               next = NEXT_INSN (next);
+             }
+           else if (active_insn_p (next)
+                    || (NOTE_P (next) && (NOTE_KIND (next)
+                                          == NOTE_INSN_SWITCH_TEXT_SECTIONS)))
+             break;
+           else
              next = NEXT_INSN (next);
-           }
          if (required)
            {
              int num = dwarf2out_cfi_label_num;
              const char *label = dwarf2out_cfi_label ();
              dw_cfi_ref xcfi;
-             rtx tmp;
 
              /* Set the location counter to the new label.  */
              xcfi = new_cfi ();
-             xcfi->dw_cfi_opc = (first ? DW_CFA_set_loc
-                                 : DW_CFA_advance_loc4);
+             xcfi->dw_cfi_opc = DW_CFA_advance_loc4;
              xcfi->dw_cfi_oprnd1.dw_cfi_addr = label;
-             VEC_safe_push (dw_cfi_ref, gc, fde->dw_fde_cfi, xcfi);
+             vec_safe_push (fde->dw_fde_cfi, xcfi);
 
-             tmp = emit_note_before (NOTE_INSN_CFI_LABEL, insn);
+             rtx_note *tmp = emit_note_before (NOTE_INSN_CFI_LABEL, insn);
              NOTE_LABEL_NUMBER (tmp) = num;
            }
 
          do
            {
-             VEC_safe_push (dw_cfi_ref, gc, fde->dw_fde_cfi, NOTE_CFI (insn));
+             if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_CFI)
+               vec_safe_push (fde->dw_fde_cfi, NOTE_CFI (insn));
              insn = NEXT_INSN (insn);
            }
          while (insn != next);
-         first = false;
        }
     }
 }
 
+static void dump_cfi_row (FILE *f, dw_cfi_row *row);
+
 /* If LABEL is the start of a trace, then initialize the state of that
    trace from CUR_TRACE and CUR_ROW.  */
 
 static void
-maybe_record_trace_start (rtx start, rtx origin, bool abnormal)
+maybe_record_trace_start (rtx_insn *start, rtx_insn *origin)
 {
   dw_trace_info *ti;
 
-  /* Sync queued data before propagating to a destination,
-     lest we propagate out-of-date data.  */
-  dwarf2out_flush_queued_reg_saves ();
-  dwarf2out_args_size (queued_args_size);
-
   ti = get_trace_info (start);
   gcc_assert (ti != NULL);
 
@@ -2404,66 +2367,127 @@ maybe_record_trace_start (rtx start, rtx origin, bool abnormal)
               (origin ? INSN_UID (origin) : 0));
     }
 
+  poly_int64 args_size = cur_trace->end_true_args_size;
   if (ti->beg_row == NULL)
     {
       /* This is the first time we've encountered this trace.  Propagate
         state across the edge and push the trace onto the work list.  */
       ti->beg_row = copy_cfi_row (cur_row);
-      /* On all abnormal edges, especially EH and non-local-goto, we take
-        care to free the pushed arguments.  */
-      if (abnormal)
-       ti->beg_row->args_size = 0;
+      ti->beg_true_args_size = args_size;
 
       ti->cfa_store = cur_trace->cfa_store;
       ti->cfa_temp = cur_trace->cfa_temp;
-      ti->regs_saved_in_regs = VEC_copy (reg_saved_in_data, heap,
-                                        cur_trace->regs_saved_in_regs);
+      ti->regs_saved_in_regs = cur_trace->regs_saved_in_regs.copy ();
 
-      VEC_safe_push (dw_trace_info_ref, heap, trace_work_list, ti);
+      trace_work_list.safe_push (ti);
 
       if (dump_file)
        fprintf (dump_file, "\tpush trace %u to worklist\n", ti->id);
     }
   else
     {
+
       /* We ought to have the same state incoming to a given trace no
         matter how we arrive at the trace.  Anything else means we've
         got some kind of optimization error.  */
-      gcc_checking_assert (cfi_row_equal_p (cur_row, ti->beg_row));
+#if CHECKING_P
+      if (!cfi_row_equal_p (cur_row, ti->beg_row))
+       {
+         if (dump_file)
+           {
+             fprintf (dump_file, "Inconsistent CFI state!\n");
+             fprintf (dump_file, "SHOULD have:\n");
+             dump_cfi_row (dump_file, ti->beg_row);
+             fprintf (dump_file, "DO have:\n");
+             dump_cfi_row (dump_file, cur_row);
+           }
+
+         gcc_unreachable ();
+       }
+#endif
+
+      /* The args_size is allowed to conflict if it isn't actually used.  */
+      if (maybe_ne (ti->beg_true_args_size, args_size))
+       ti->args_size_undefined = true;
+    }
+}
+
+/* Similarly, but handle the args_size and CFA reset across EH
+   and non-local goto edges.  */
+
+static void
+maybe_record_trace_start_abnormal (rtx_insn *start, rtx_insn *origin)
+{
+  poly_int64 save_args_size, delta;
+  dw_cfa_location save_cfa;
+
+  save_args_size = cur_trace->end_true_args_size;
+  if (known_eq (save_args_size, 0))
+    {
+      maybe_record_trace_start (start, origin);
+      return;
+    }
+
+  delta = -save_args_size;
+  cur_trace->end_true_args_size = 0;
+
+  save_cfa = cur_row->cfa;
+  if (cur_row->cfa.reg == dw_stack_pointer_regnum)
+    {
+      /* Convert a change in args_size (always a positive in the
+        direction of stack growth) to a change in stack pointer.  */
+      if (!STACK_GROWS_DOWNWARD)
+       delta = -delta;
+
+      cur_row->cfa.offset += delta;
     }
+  
+  maybe_record_trace_start (start, origin);
+
+  cur_trace->end_true_args_size = save_args_size;
+  cur_row->cfa = save_cfa;
 }
 
 /* Propagate CUR_TRACE state to the destinations implied by INSN.  */
 /* ??? Sadly, this is in large part a duplicate of make_edges.  */
 
 static void
-create_trace_edges (rtx insn)
+create_trace_edges (rtx_insn *insn)
 {
-  rtx tmp, lab;
+  rtx tmp;
   int i, n;
 
   if (JUMP_P (insn))
     {
+      rtx_jump_table_data *table;
+
       if (find_reg_note (insn, REG_NON_LOCAL_GOTO, NULL_RTX))
-       ;
-      else if (tablejump_p (insn, NULL, &tmp))
-       {
-         rtvec vec;
+       return;
 
-         tmp = PATTERN (tmp);
-         vec = XVEC (tmp, GET_CODE (tmp) == ADDR_DIFF_VEC);
+      if (tablejump_p (insn, NULL, &table))
+       {
+         rtvec vec = table->get_labels ();
 
          n = GET_NUM_ELEM (vec);
          for (i = 0; i < n; ++i)
            {
-             lab = XEXP (RTVEC_ELT (vec, i), 0);
-             maybe_record_trace_start (lab, insn, false);
+             rtx_insn *lab = as_a <rtx_insn *> (XEXP (RTVEC_ELT (vec, i), 0));
+             maybe_record_trace_start (lab, insn);
+           }
+
+         /* Handle casesi dispatch insns.  */
+         if ((tmp = tablejump_casesi_pattern (insn)) != NULL_RTX)
+           {
+             rtx_insn * lab = label_ref_label (XEXP (SET_SRC (tmp), 2));
+             maybe_record_trace_start (lab, insn);
            }
        }
       else if (computed_jump_p (insn))
        {
-         for (lab = forced_labels; lab; lab = XEXP (lab, 1))
-           maybe_record_trace_start (XEXP (lab, 0), insn, true);
+         rtx_insn *temp;
+         unsigned int i;
+         FOR_EACH_VEC_SAFE_ELT (forced_labels, i, temp)
+           maybe_record_trace_start (temp, insn);
        }
       else if (returnjump_p (insn))
        ;
@@ -2472,15 +2496,16 @@ create_trace_edges (rtx insn)
          n = ASM_OPERANDS_LABEL_LENGTH (tmp);
          for (i = 0; i < n; ++i)
            {
-             lab = XEXP (ASM_OPERANDS_LABEL (tmp, i), 0);
-             maybe_record_trace_start (lab, insn, true);
+             rtx_insn *lab =
+               as_a <rtx_insn *> (XEXP (ASM_OPERANDS_LABEL (tmp, i), 0));
+             maybe_record_trace_start (lab, insn);
            }
        }
       else
        {
-         lab = JUMP_LABEL (insn);
+         rtx_insn *lab = JUMP_LABEL_AS_INSN (insn);
          gcc_assert (lab != NULL);
-         maybe_record_trace_start (lab, insn, false);
+         maybe_record_trace_start (lab, insn);
        }
     }
   else if (CALL_P (insn))
@@ -2491,15 +2516,16 @@ create_trace_edges (rtx insn)
 
       /* Process non-local goto edges.  */
       if (can_nonlocal_goto (insn))
-       for (lab = nonlocal_goto_handler_labels; lab; lab = XEXP (lab, 1))
-         maybe_record_trace_start (XEXP (lab, 0), insn, true);
+       for (rtx_insn_list *lab = nonlocal_goto_handler_labels;
+            lab;
+            lab = lab->next ())
+         maybe_record_trace_start_abnormal (lab->insn (), insn);
     }
-  else if (GET_CODE (PATTERN (insn)) == SEQUENCE)
+  else if (rtx_sequence *seq = dyn_cast <rtx_sequence *> (PATTERN (insn)))
     {
-      rtx seq = PATTERN (insn);
-      int i, n = XVECLEN (seq, 0);
+      int i, n = seq->len ();
       for (i = 0; i < n; ++i)
-       create_trace_edges (XVECEXP (seq, 0, i));
+       create_trace_edges (seq->insn (i));
       return;
     }
 
@@ -2508,17 +2534,28 @@ create_trace_edges (rtx insn)
     {
       eh_landing_pad lp = get_eh_landing_pad_from_rtx (insn);
       if (lp)
-       maybe_record_trace_start (lp->landing_pad, insn, true);
+       maybe_record_trace_start_abnormal (lp->landing_pad, insn);
     }
 }
 
+/* A subroutine of scan_trace.  Do what needs to be done "after" INSN.  */
+
+static void
+scan_insn_after (rtx_insn *insn)
+{
+  if (RTX_FRAME_RELATED_P (insn))
+    dwarf2out_frame_debug (insn);
+  notice_args_size (insn);
+}
+
 /* Scan the trace beginning at INSN and create the CFI notes for the
    instructions therein.  */
 
 static void
-scan_trace (dw_trace_info *trace)
+scan_trace (dw_trace_info *trace, bool entry)
 {
-  rtx insn = trace->head;
+  rtx_insn *prev, *insn = trace->head;
+  dw_cfa_location this_cfa;
 
   if (dump_file)
     fprintf (dump_file, "Processing trace %u : start at %s %d\n",
@@ -2526,72 +2563,179 @@ scan_trace (dw_trace_info *trace)
             INSN_UID (insn));
 
   trace->end_row = copy_cfi_row (trace->beg_row);
+  trace->end_true_args_size = trace->beg_true_args_size;
 
   cur_trace = trace;
   cur_row = trace->end_row;
-  queued_args_size = cur_row->args_size;
 
-  for (insn = NEXT_INSN (insn); insn ; insn = NEXT_INSN (insn))
+  this_cfa = cur_row->cfa;
+  cur_cfa = &this_cfa;
+
+  /* If the current function starts with a non-standard incoming frame
+     sp offset, emit a note before the first instruction.  */
+  if (entry
+      && DEFAULT_INCOMING_FRAME_SP_OFFSET != INCOMING_FRAME_SP_OFFSET)
+    {
+      add_cfi_insn = insn;
+      gcc_assert (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_DELETED);
+      this_cfa.offset = INCOMING_FRAME_SP_OFFSET;
+      def_cfa_1 (&this_cfa);
+    }
+
+  for (prev = insn, insn = NEXT_INSN (insn);
+       insn;
+       prev = insn, insn = NEXT_INSN (insn))
     {
-      rtx pat;
+      rtx_insn *control;
 
-      add_cfi_insn = PREV_INSN (insn);
+      /* Do everything that happens "before" the insn.  */
+      add_cfi_insn = prev;
 
       /* Notice the end of a trace.  */
-      if (BARRIER_P (insn) || save_point_p (insn))
+      if (BARRIER_P (insn))
+       {
+         /* Don't bother saving the unneeded queued registers at all.  */
+         queued_reg_saves.truncate (0);
+         break;
+       }
+      if (save_point_p (insn))
        {
-         dwarf2out_flush_queued_reg_saves ();
-         dwarf2out_args_size (queued_args_size);
-
          /* Propagate across fallthru edges.  */
-         if (!BARRIER_P (insn))
-           maybe_record_trace_start (insn, NULL, false);
+         dwarf2out_flush_queued_reg_saves ();
+         maybe_record_trace_start (insn, NULL);
          break;
        }
 
       if (DEBUG_INSN_P (insn) || !inside_basic_block_p (insn))
        continue;
 
-      pat = PATTERN (insn);
-      if (asm_noperands (pat) >= 0)
+      /* Handle all changes to the row state.  Sequences require special
+        handling for the positioning of the notes.  */
+      if (rtx_sequence *pat = dyn_cast <rtx_sequence *> (PATTERN (insn)))
        {
-         dwarf2out_frame_debug (insn, false);
+         rtx_insn *elt;
+         int i, n = pat->len ();
+
+         control = pat->insn (0);
+         if (can_throw_internal (control))
+           notice_eh_throw (control);
+         dwarf2out_flush_queued_reg_saves ();
+
+         if (JUMP_P (control) && INSN_ANNULLED_BRANCH_P (control))
+           {
+             /* ??? Hopefully multiple delay slots are not annulled.  */
+             gcc_assert (n == 2);
+             gcc_assert (!RTX_FRAME_RELATED_P (control));
+             gcc_assert (!find_reg_note (control, REG_ARGS_SIZE, NULL));
+
+             elt = pat->insn (1);
+
+             if (INSN_FROM_TARGET_P (elt))
+               {
+                 cfi_vec save_row_reg_save;
+
+                 /* If ELT is an instruction from target of an annulled
+                    branch, the effects are for the target only and so
+                    the args_size and CFA along the current path
+                    shouldn't change.  */
+                 add_cfi_insn = NULL;
+                 poly_int64 restore_args_size = cur_trace->end_true_args_size;
+                 cur_cfa = &cur_row->cfa;
+                 save_row_reg_save = vec_safe_copy (cur_row->reg_save);
+
+                 scan_insn_after (elt);
+
+                 /* ??? Should we instead save the entire row state?  */
+                 gcc_assert (!queued_reg_saves.length ());
+
+                 create_trace_edges (control);
+
+                 cur_trace->end_true_args_size = restore_args_size;
+                 cur_row->cfa = this_cfa;
+                 cur_row->reg_save = save_row_reg_save;
+                 cur_cfa = &this_cfa;
+               }
+             else
+               {
+                 /* If ELT is a annulled branch-taken instruction (i.e.
+                    executed only when branch is not taken), the args_size
+                    and CFA should not change through the jump.  */
+                 create_trace_edges (control);
+
+                 /* Update and continue with the trace.  */
+                 add_cfi_insn = insn;
+                 scan_insn_after (elt);
+                 def_cfa_1 (&this_cfa);
+               }
+             continue;
+           }
+
+         /* The insns in the delay slot should all be considered to happen
+            "before" a call insn.  Consider a call with a stack pointer
+            adjustment in the delay slot.  The backtrace from the callee
+            should include the sp adjustment.  Unfortunately, that leaves
+            us with an unavoidable unwinding error exactly at the call insn
+            itself.  For jump insns we'd prefer to avoid this error by
+            placing the notes after the sequence.  */
+         if (JUMP_P (control))
+           add_cfi_insn = insn;
+
+         for (i = 1; i < n; ++i)
+           {
+             elt = pat->insn (i);
+             scan_insn_after (elt);
+           }
+
+         /* Make sure any register saves are visible at the jump target.  */
+         dwarf2out_flush_queued_reg_saves ();
+         any_cfis_emitted = false;
+
+          /* However, if there is some adjustment on the call itself, e.g.
+            a call_pop, that action should be considered to happen after
+            the call returns.  */
          add_cfi_insn = insn;
+         scan_insn_after (control);
        }
       else
        {
-         if (GET_CODE (pat) == SEQUENCE)
+         /* Flush data before calls and jumps, and of course if necessary.  */
+         if (can_throw_internal (insn))
            {
-             int i, n = XVECLEN (pat, 0);
-             for (i = 1; i < n; ++i)
-               dwarf2out_frame_debug (XVECEXP (pat, 0, i), false);
+             notice_eh_throw (insn);
+             dwarf2out_flush_queued_reg_saves ();
            }
-
-          if (CALL_P (insn))
-           dwarf2out_frame_debug (insn, false);
-          else if (find_reg_note (insn, REG_CFA_FLUSH_QUEUE, NULL)
-                  || (cfun->can_throw_non_call_exceptions
-                      && can_throw_internal (insn)))
+         else if (!NONJUMP_INSN_P (insn)
+                  || clobbers_queued_reg_save (insn)
+                  || find_reg_note (insn, REG_CFA_FLUSH_QUEUE, NULL))
            dwarf2out_flush_queued_reg_saves ();
+         any_cfis_emitted = false;
 
-         /* Do not separate tablejump insns from their ADDR_DIFF_VEC.
-            Putting the note after the VEC should be ok.  */
-         if (!tablejump_p (insn, NULL, &add_cfi_insn))
-           add_cfi_insn = insn;
-
-         dwarf2out_frame_debug (insn, true);
+         add_cfi_insn = insn;
+         scan_insn_after (insn);
+         control = insn;
        }
 
+      /* Between frame-related-p and args_size we might have otherwise
+        emitted two cfa adjustments.  Do it now.  */
+      def_cfa_1 (&this_cfa);
+
+      /* Minimize the number of advances by emitting the entire queue
+        once anything is emitted.  */
+      if (any_cfis_emitted
+         || find_reg_note (insn, REG_CFA_FLUSH_QUEUE, NULL))
+       dwarf2out_flush_queued_reg_saves ();
+
       /* Note that a test for control_flow_insn_p does exactly the
         same tests as are done to actually create the edges.  So
         always call the routine and let it not create edges for
         non-control-flow insns.  */
-      create_trace_edges (insn);
+      create_trace_edges (control);
     }
 
   add_cfi_insn = NULL;
   cur_row = NULL;
   cur_trace = NULL;
+  cur_cfa = NULL;
 }
 
 /* Scan the function and create the initial set of CFI notes.  */
@@ -2601,29 +2745,29 @@ create_cfi_notes (void)
 {
   dw_trace_info *ti;
 
-  gcc_checking_assert (queued_reg_saves == NULL);
-  gcc_checking_assert (trace_work_list == NULL);
+  gcc_checking_assert (!queued_reg_saves.exists ());
+  gcc_checking_assert (!trace_work_list.exists ());
 
   /* Always begin at the entry trace.  */
-  ti = VEC_index (dw_trace_info, trace_info, 0);
-  scan_trace (ti);
+  ti = &trace_info[0];
+  scan_trace (ti, true);
 
-  while (!VEC_empty (dw_trace_info_ref, trace_work_list))
+  while (!trace_work_list.is_empty ())
     {
-      ti = VEC_pop (dw_trace_info_ref, trace_work_list);
-      scan_trace (ti);
+      ti = trace_work_list.pop ();
+      scan_trace (ti, false);
     }
 
-  VEC_free (queued_reg_save, heap, queued_reg_saves);
-  VEC_free (dw_trace_info_ref, heap, trace_work_list);
+  queued_reg_saves.release ();
+  trace_work_list.release ();
 }
 
 /* Return the insn before the first NOTE_INSN_CFI after START.  */
 
-static rtx
-before_next_cfi_note (rtx start)
+static rtx_insn *
+before_next_cfi_note (rtx_insn *start)
 {
-  rtx prev = start;
+  rtx_insn *prev = start;
   while (start)
     {
       if (NOTE_P (start) && NOTE_KIND (start) == NOTE_INSN_CFI)
@@ -2639,7 +2783,7 @@ before_next_cfi_note (rtx start)
 static void
 connect_traces (void)
 {
-  unsigned i, n = VEC_length (dw_trace_info, trace_info);
+  unsigned i, n;
   dw_trace_info *prev_ti, *ti;
 
   /* ??? Ideally, we should have both queued and processed every trace.
@@ -2650,27 +2794,22 @@ connect_traces (void)
      these are not "real" instructions, and should not be considered.
      This could be generically useful for tablejump data as well.  */
   /* Remove all unprocessed traces from the list.  */
-  for (i = n - 1; i > 0; --i)
-    {
-      ti = VEC_index (dw_trace_info, trace_info, i);
-      if (ti->beg_row == NULL)
-       {
-         VEC_ordered_remove (dw_trace_info, trace_info, i);
-         n -= 1;
-       }
-      else
-       gcc_assert (ti->end_row != NULL);
-    }
+  unsigned ix, ix2;
+  VEC_ORDERED_REMOVE_IF_FROM_TO (trace_info, ix, ix2, ti, 1,
+                                trace_info.length (), ti->beg_row == NULL);
+  FOR_EACH_VEC_ELT (trace_info, ix, ti)
+    gcc_assert (ti->end_row != NULL);
 
   /* Work from the end back to the beginning.  This lets us easily insert
      remember/restore_state notes in the correct order wrt other notes.  */
-  prev_ti = VEC_index (dw_trace_info, trace_info, n - 1);
+  n = trace_info.length ();
+  prev_ti = &trace_info[n - 1];
   for (i = n - 1; i > 0; --i)
     {
       dw_cfi_row *old_row;
 
       ti = prev_ti;
-      prev_ti = VEC_index (dw_trace_info, trace_info, i - 1);
+      prev_ti = &trace_info[i - 1];
 
       add_cfi_insn = ti->head;
 
@@ -2718,7 +2857,7 @@ connect_traces (void)
 
       if (dump_file && add_cfi_insn != ti->head)
        {
-         rtx note;
+         rtx_insn *note;
 
          fprintf (dump_file, "Fixup between trace %u and %u:\n",
                   prev_ti->id, ti->id);
@@ -2733,6 +2872,38 @@ connect_traces (void)
          while (note != add_cfi_insn);
        }
     }
+
+  /* Connect args_size between traces that have can_throw_internal insns.  */
+  if (cfun->eh->lp_array)
+    {
+      poly_int64 prev_args_size = 0;
+
+      for (i = 0; i < n; ++i)
+       {
+         ti = &trace_info[i];
+
+         if (ti->switch_sections)
+           prev_args_size = 0;
+
+         if (ti->eh_head == NULL)
+           continue;
+
+         /* We require either the incoming args_size values to match or the
+            presence of an insn setting it before the first EH insn.  */
+         gcc_assert (!ti->args_size_undefined || ti->args_size_defined_for_eh);
+
+         /* In the latter case, we force the creation of a CFI note.  */
+         if (ti->args_size_undefined
+             || maybe_ne (ti->beg_delay_args_size, prev_args_size))
+           {
+             /* ??? Search back to previous CFI note.  */
+             add_cfi_insn = PREV_INSN (ti->eh_head);
+             add_cfi_args_size (ti->beg_delay_args_size);
+           }
+
+         prev_args_size = ti->end_delay_args_size;
+       }
+    }
 }
 
 /* Set up the pseudo-cfg of instruction traces, as described at the
@@ -2742,23 +2913,22 @@ static void
 create_pseudo_cfg (void)
 {
   bool saw_barrier, switch_sections;
-  dw_trace_info *ti;
-  rtx insn;
+  dw_trace_info ti;
+  rtx_insn *insn;
   unsigned i;
 
   /* The first trace begins at the start of the function,
      and begins with the CIE row state.  */
-  trace_info = VEC_alloc (dw_trace_info, heap, 16);
-  ti = VEC_quick_push (dw_trace_info, trace_info, NULL);
-
-  memset (ti, 0, sizeof (*ti));
-  ti->head = get_insns ();
-  ti->beg_row = cie_cfi_row;
-  ti->cfa_store = cie_cfi_row->cfa;
-  ti->cfa_temp.reg = INVALID_REGNUM;
+  trace_info.create (16);
+  memset (&ti, 0, sizeof (ti));
+  ti.head = get_insns ();
+  ti.beg_row = cie_cfi_row;
+  ti.cfa_store = cie_cfi_row->cfa;
+  ti.cfa_temp.reg = INVALID_REGNUM;
+  trace_info.quick_push (ti);
+
   if (cie_return_save)
-    VEC_safe_push (reg_saved_in_data, heap,
-                  ti->regs_saved_in_regs, cie_return_save);
+    ti.regs_saved_in_regs.safe_push (*cie_return_save);
 
   /* Walk all the insns, collecting start of trace locations.  */
   saw_barrier = false;
@@ -2780,11 +2950,11 @@ create_pseudo_cfg (void)
       else if (save_point_p (insn)
               && (LABEL_P (insn) || !saw_barrier))
        {
-         ti = VEC_safe_push (dw_trace_info, heap, trace_info, NULL);
-         memset (ti, 0, sizeof (*ti));
-         ti->head = insn;
-         ti->switch_sections = switch_sections;
-         ti->id = VEC_length (dw_trace_info, trace_info) - 1;
+         memset (&ti, 0, sizeof (ti));
+         ti.head = insn;
+         ti.switch_sections = switch_sections;
+         ti.id = trace_info.length ();
+         trace_info.safe_push (ti);
 
          saw_barrier = false;
          switch_sections = false;
@@ -2793,21 +2963,21 @@ create_pseudo_cfg (void)
 
   /* Create the trace index after we've finished building trace_info,
      avoiding stale pointer problems due to reallocation.  */
-  trace_index = htab_create (VEC_length (dw_trace_info, trace_info),
-                            dw_trace_info_hash, dw_trace_info_eq, NULL);
-  FOR_EACH_VEC_ELT (dw_trace_info, trace_info, i, ti)
+  trace_index
+    = new hash_table<trace_info_hasher> (trace_info.length ());
+  dw_trace_info *tp;
+  FOR_EACH_VEC_ELT (trace_info, i, tp)
     {
-      void **slot;
+      dw_trace_info **slot;
 
       if (dump_file)
-       fprintf (dump_file, "Creating trace %u : start at %s %d%s\n", i,
-                rtx_name[(int) GET_CODE (ti->head)], INSN_UID (ti->head),
-                ti->switch_sections ? " (section switch)" : "");
+       fprintf (dump_file, "Creating trace %u : start at %s %d%s\n", tp->id,
+                rtx_name[(int) GET_CODE (tp->head)], INSN_UID (tp->head),
+                tp->switch_sections ? " (section switch)" : "");
 
-      slot = htab_find_slot_with_hash (trace_index, ti,
-                                      INSN_UID (ti->head), INSERT);
+      slot = trace_index->find_slot_with_hash (tp, INSN_UID (tp->head), INSERT);
       gcc_assert (*slot == NULL);
-      *slot = (void *) ti;
+      *slot = tp;
     }
 }
 
@@ -2818,7 +2988,7 @@ static void
 initial_return_save (rtx rtl)
 {
   unsigned int reg = INVALID_REGNUM;
-  HOST_WIDE_INT offset = 0;
+  poly_int64 offset = 0;
 
   switch (GET_CODE (rtl))
     {
@@ -2839,12 +3009,12 @@ initial_return_save (rtx rtl)
 
        case PLUS:
          gcc_assert (REGNO (XEXP (rtl, 0)) == STACK_POINTER_REGNUM);
-         offset = INTVAL (XEXP (rtl, 1));
+         offset = rtx_to_poly_int64 (XEXP (rtl, 1));
          break;
 
        case MINUS:
          gcc_assert (REGNO (XEXP (rtl, 0)) == STACK_POINTER_REGNUM);
-         offset = -INTVAL (XEXP (rtl, 1));
+         offset = -rtx_to_poly_int64 (XEXP (rtl, 1));
          break;
 
        default:
@@ -2880,18 +3050,22 @@ create_cie_data (void)
   dw_trace_info cie_trace;
 
   dw_stack_pointer_regnum = DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM);
-  dw_frame_pointer_regnum = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
 
-  memset (&cie_trace, 0, sizeof(cie_trace));
+  memset (&cie_trace, 0, sizeof (cie_trace));
   cur_trace = &cie_trace;
 
   add_cfi_vec = &cie_cfi_vec;
   cie_cfi_row = cur_row = new_cfi_row ();
 
   /* On entry, the Canonical Frame Address is at SP.  */
-  memset(&loc, 0, sizeof (loc));
+  memset (&loc, 0, sizeof (loc));
   loc.reg = dw_stack_pointer_regnum;
-  loc.offset = INCOMING_FRAME_SP_OFFSET;
+  /* create_cie_data is called just once per TU, and when using .cfi_startproc
+     is even done by the assembler rather than the compiler.  If the target
+     has different incoming frame sp offsets depending on what kind of
+     function it is, use a single constant offset for the target and
+     if needed, adjust before the first instruction in insn stream.  */
+  loc.offset = DEFAULT_INCOMING_FRAME_SP_OFFSET;
   def_cfa_1 (&loc);
 
   if (targetm.debug_unwind_info () == UI_DWARF2
@@ -2907,15 +3081,14 @@ create_cie_data (void)
         the DW_CFA_offset against the return column, not the intermediate
         save register.  Save the contents of regs_saved_in_regs so that
         we can re-initialize it at the start of each function.  */
-      switch (VEC_length (reg_saved_in_data, cie_trace.regs_saved_in_regs))
+      switch (cie_trace.regs_saved_in_regs.length ())
        {
        case 0:
          break;
        case 1:
-         cie_return_save = ggc_alloc_reg_saved_in_data ();
-         *cie_return_save = *VEC_index (reg_saved_in_data,
-                                        cie_trace.regs_saved_in_regs, 0);
-         VEC_free (reg_saved_in_data, heap, cie_trace.regs_saved_in_regs);
+         cie_return_save = ggc_alloc<reg_saved_in_data> ();
+         *cie_return_save = cie_trace.regs_saved_in_regs[0];
+         cie_trace.regs_saved_in_regs.release ();
          break;
        default:
          gcc_unreachable ();
@@ -2934,6 +3107,9 @@ create_cie_data (void)
 static unsigned int
 execute_dwarf2_frame (void)
 {
+  /* Different HARD_FRAME_POINTER_REGNUM might coexist in the same file.  */
+  dw_frame_pointer_regnum = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM);
+
   /* The first time we're called, compute the incoming frame state.  */
   if (cie_cfi_vec == NULL)
     create_cie_data ();
@@ -2952,12 +3128,12 @@ execute_dwarf2_frame (void)
     size_t i;
     dw_trace_info *ti;
 
-    FOR_EACH_VEC_ELT (dw_trace_info, trace_info, i, ti)
-      VEC_free (reg_saved_in_data, heap, ti->regs_saved_in_regs);
+    FOR_EACH_VEC_ELT (trace_info, i, ti)
+      ti->regs_saved_in_regs.release ();
   }
-  VEC_free (dw_trace_info, heap, trace_info);
+  trace_info.release ();
 
-  htab_delete (trace_index);
+  delete trace_index;
   trace_index = NULL;
 
   return 0;
@@ -2968,72 +3144,12 @@ execute_dwarf2_frame (void)
 static const char *
 dwarf_cfi_name (unsigned int cfi_opc)
 {
-  switch (cfi_opc)
-    {
-    case DW_CFA_advance_loc:
-      return "DW_CFA_advance_loc";
-    case DW_CFA_offset:
-      return "DW_CFA_offset";
-    case DW_CFA_restore:
-      return "DW_CFA_restore";
-    case DW_CFA_nop:
-      return "DW_CFA_nop";
-    case DW_CFA_set_loc:
-      return "DW_CFA_set_loc";
-    case DW_CFA_advance_loc1:
-      return "DW_CFA_advance_loc1";
-    case DW_CFA_advance_loc2:
-      return "DW_CFA_advance_loc2";
-    case DW_CFA_advance_loc4:
-      return "DW_CFA_advance_loc4";
-    case DW_CFA_offset_extended:
-      return "DW_CFA_offset_extended";
-    case DW_CFA_restore_extended:
-      return "DW_CFA_restore_extended";
-    case DW_CFA_undefined:
-      return "DW_CFA_undefined";
-    case DW_CFA_same_value:
-      return "DW_CFA_same_value";
-    case DW_CFA_register:
-      return "DW_CFA_register";
-    case DW_CFA_remember_state:
-      return "DW_CFA_remember_state";
-    case DW_CFA_restore_state:
-      return "DW_CFA_restore_state";
-    case DW_CFA_def_cfa:
-      return "DW_CFA_def_cfa";
-    case DW_CFA_def_cfa_register:
-      return "DW_CFA_def_cfa_register";
-    case DW_CFA_def_cfa_offset:
-      return "DW_CFA_def_cfa_offset";
-
-    /* DWARF 3 */
-    case DW_CFA_def_cfa_expression:
-      return "DW_CFA_def_cfa_expression";
-    case DW_CFA_expression:
-      return "DW_CFA_expression";
-    case DW_CFA_offset_extended_sf:
-      return "DW_CFA_offset_extended_sf";
-    case DW_CFA_def_cfa_sf:
-      return "DW_CFA_def_cfa_sf";
-    case DW_CFA_def_cfa_offset_sf:
-      return "DW_CFA_def_cfa_offset_sf";
+  const char *name = get_DW_CFA_name (cfi_opc);
 
-    /* SGI/MIPS specific */
-    case DW_CFA_MIPS_advance_loc8:
-      return "DW_CFA_MIPS_advance_loc8";
-
-    /* GNU extensions */
-    case DW_CFA_GNU_window_save:
-      return "DW_CFA_GNU_window_save";
-    case DW_CFA_GNU_args_size:
-      return "DW_CFA_GNU_args_size";
-    case DW_CFA_GNU_negative_offset_extended:
-      return "DW_CFA_GNU_negative_offset_extended";
+  if (name != NULL)
+    return name;
 
-    default:
-      return "DW_CFA_<unknown>";
-    }
+  return "DW_CFA_<unknown>";
 }
 
 /* This routine will generate the correct assembly data for a location
@@ -3045,7 +3161,8 @@ output_cfa_loc (dw_cfi_ref cfi, int for_eh)
   dw_loc_descr_ref loc;
   unsigned long size;
 
-  if (cfi->dw_cfi_opc == DW_CFA_expression)
+  if (cfi->dw_cfi_opc == DW_CFA_expression
+      || cfi->dw_cfi_opc == DW_CFA_val_expression)
     {
       unsigned r =
        DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, for_eh);
@@ -3071,7 +3188,8 @@ output_cfa_loc_raw (dw_cfi_ref cfi)
   dw_loc_descr_ref loc;
   unsigned long size;
 
-  if (cfi->dw_cfi_opc == DW_CFA_expression)
+  if (cfi->dw_cfi_opc == DW_CFA_expression
+      || cfi->dw_cfi_opc == DW_CFA_val_expression)
     {
       unsigned r =
        DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1);
@@ -3218,6 +3336,7 @@ output_cfi (dw_cfi_ref cfi, dw_fde_ref fde, int for_eh)
 
        case DW_CFA_def_cfa_expression:
        case DW_CFA_expression:
+       case DW_CFA_val_expression:
          output_cfa_loc (cfi, for_eh);
          break;
 
@@ -3258,7 +3377,7 @@ output_cfi_directive (FILE *f, dw_cfi_ref cfi)
     case DW_CFA_offset_extended:
     case DW_CFA_offset_extended_sf:
       r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1);
-      fprintf (f, "\t.cfi_offset %lu, "HOST_WIDE_INT_PRINT_DEC"\n",
+      fprintf (f, "\t.cfi_offset %lu, " HOST_WIDE_INT_PRINT_DEC"\n",
               r, cfi->dw_cfi_oprnd2.dw_cfi_offset);
       break;
 
@@ -3281,7 +3400,7 @@ output_cfi_directive (FILE *f, dw_cfi_ref cfi)
     case DW_CFA_def_cfa:
     case DW_CFA_def_cfa_sf:
       r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1);
-      fprintf (f, "\t.cfi_def_cfa %lu, "HOST_WIDE_INT_PRINT_DEC"\n",
+      fprintf (f, "\t.cfi_def_cfa %lu, " HOST_WIDE_INT_PRINT_DEC"\n",
               r, cfi->dw_cfi_oprnd2.dw_cfi_offset);
       break;
 
@@ -3316,13 +3435,13 @@ output_cfi_directive (FILE *f, dw_cfi_ref cfi)
          fprintf (f, "\t.cfi_escape %#x,", DW_CFA_GNU_args_size);
          dw2_asm_output_data_uleb128_raw (cfi->dw_cfi_oprnd1.dw_cfi_offset);
          if (flag_debug_asm)
-           fprintf (f, "\t%s args_size "HOST_WIDE_INT_PRINT_DEC,
+           fprintf (f, "\t%s args_size " HOST_WIDE_INT_PRINT_DEC,
                     ASM_COMMENT_START, cfi->dw_cfi_oprnd1.dw_cfi_offset);
          fputc ('\n', f);
        }
       else
        {
-         fprintf (f, "\t.cfi_GNU_args_size "HOST_WIDE_INT_PRINT_DEC "\n",
+         fprintf (f, "\t.cfi_GNU_args_size " HOST_WIDE_INT_PRINT_DEC "\n",
                   cfi->dw_cfi_oprnd1.dw_cfi_offset);
        }
       break;
@@ -3332,16 +3451,13 @@ output_cfi_directive (FILE *f, dw_cfi_ref cfi)
       break;
 
     case DW_CFA_def_cfa_expression:
-      if (f != asm_out_file)
-       {
-         fprintf (f, "\t.cfi_def_cfa_expression ...\n");
-         break;
-       }
-      /* FALLTHRU */
     case DW_CFA_expression:
+    case DW_CFA_val_expression:
       if (f != asm_out_file)
        {
-         fprintf (f, "\t.cfi_cfa_expression ...\n");
+         fprintf (f, "\t.cfi_%scfa_%sexpression ...\n",
+                  cfi->dw_cfi_opc == DW_CFA_def_cfa_expression ? "def_" : "",
+                  cfi->dw_cfi_opc == DW_CFA_val_expression ? "val_" : "");
          break;
        }
       fprintf (f, "\t.cfi_escape %#x,", cfi->dw_cfi_opc);
@@ -3371,18 +3487,15 @@ dump_cfi_row (FILE *f, dw_cfi_row *row)
   if (!cfi)
     {
       dw_cfa_location dummy;
-      memset(&dummy, 0, sizeof(dummy));
+      memset (&dummy, 0, sizeof (dummy));
       dummy.reg = INVALID_REGNUM;
       cfi = def_cfa_0 (&dummy, &row->cfa);
     }
   output_cfi_directive (f, cfi);
 
-  FOR_EACH_VEC_ELT (dw_cfi_ref, row->reg_save, i, cfi)
+  FOR_EACH_VEC_SAFE_ELT (row->reg_save, i, cfi)
     if (cfi)
       output_cfi_directive (f, cfi);
-
-  fprintf (f, "\t.cfi_GNU_args_size "HOST_WIDE_INT_PRINT_DEC "\n",
-          row->args_size);
 }
 
 void debug_cfi_row (dw_cfi_row *row);
@@ -3398,6 +3511,17 @@ debug_cfi_row (dw_cfi_row *row)
    This variable is tri-state, with 0 unset, >0 true, <0 false.  */
 static GTY(()) signed char saved_do_cfi_asm = 0;
 
+/* Decide whether to emit EH frame unwind information for the current
+   translation unit.  */
+
+bool
+dwarf2out_do_eh_frame (void)
+{
+  return
+    (flag_unwind_tables || flag_exceptions)
+    && targetm_common.except_unwind_info (&global_options) == UI_DWARF2;
+}
+
 /* Decide whether we want to emit frame unwind information for the current
    translation unit.  */
 
@@ -3416,8 +3540,7 @@ dwarf2out_do_frame (void)
   if (targetm.debug_unwind_info () == UI_DWARF2)
     return true;
 
-  if ((flag_unwind_tables || flag_exceptions)
-      && targetm_common.except_unwind_info (&global_options) == UI_DWARF2)
+  if (dwarf2out_do_eh_frame ())
     return true;
 
   return false;
@@ -3430,10 +3553,6 @@ dwarf2out_do_cfi_asm (void)
 {
   int enc;
 
-#ifdef MIPS_DEBUGGING_INFO
-  return false;
-#endif
-
   if (saved_do_cfi_asm != 0)
     return saved_do_cfi_asm > 0;
 
@@ -3456,9 +3575,7 @@ dwarf2out_do_cfi_asm (void)
 
   /* If we can't get the assembler to emit only .debug_frame, and we don't need
      dwarf2 unwind info for exceptions, then emit .debug_frame by hand.  */
-  if (!HAVE_GAS_CFI_SECTIONS_DIRECTIVE
-      && !flag_unwind_tables && !flag_exceptions
-      && targetm_common.except_unwind_info (&global_options) != UI_DWARF2)
+  if (!HAVE_GAS_CFI_SECTIONS_DIRECTIVE && !dwarf2out_do_eh_frame ())
     return false;
 
   /* Success!  */
@@ -3466,14 +3583,41 @@ dwarf2out_do_cfi_asm (void)
   return true;
 }
 
-static bool
-gate_dwarf2_frame (void)
+namespace {
+
+const pass_data pass_data_dwarf2_frame =
+{
+  RTL_PASS, /* type */
+  "dwarf2", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_FINAL, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_dwarf2_frame : public rtl_opt_pass
+{
+public:
+  pass_dwarf2_frame (gcc::context *ctxt)
+    : rtl_opt_pass (pass_data_dwarf2_frame, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *);
+  virtual unsigned int execute (function *) { return execute_dwarf2_frame (); }
+
+}; // class pass_dwarf2_frame
+
+bool
+pass_dwarf2_frame::gate (function *)
 {
-#ifndef HAVE_prologue
   /* Targets which still implement the prologue in assembler text
      cannot use the generic dwarf2 unwinding.  */
-  return false;
-#endif
+  if (!targetm.have_prologue ())
+    return false;
 
   /* ??? What to do for UI_TARGET unwinding?  They might be able to benefit
      from the optimized shrink-wrapping annotations that we will compute.
@@ -3481,23 +3625,12 @@ gate_dwarf2_frame (void)
   return dwarf2out_do_frame ();
 }
 
-struct rtl_opt_pass pass_dwarf2_frame =
-{
- {
-  RTL_PASS,
-  "dwarf2",                    /* name */
-  gate_dwarf2_frame,           /* gate */
-  execute_dwarf2_frame,                /* execute */
-  NULL,                                /* sub */
-  NULL,                                /* next */
-  0,                           /* static_pass_number */
-  TV_FINAL,                    /* tv_id */
-  0,                           /* properties_required */
-  0,                           /* properties_provided */
-  0,                           /* properties_destroyed */
-  0,                           /* todo_flags_start */
-  0                            /* todo_flags_finish */
- }
-};
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_dwarf2_frame (gcc::context *ctxt)
+{
+  return new pass_dwarf2_frame (ctxt);
+}
 
 #include "gt-dwarf2cfi.h"