]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/dwarf2cfi.c
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / dwarf2cfi.c
index ec2acc961d7516dad0f68cccc354b74554f6e19c..df9b625f5bcc255404066dada43f8606776553db 100644 (file)
@@ -1,5 +1,5 @@
 /* Dwarf2 Call Frame Information helper routines.
-   Copyright (C) 1992-2015 Free Software Foundation, Inc.
+   Copyright (C) 1992-2021 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -20,52 +20,26 @@ 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 "target.h"
+#include "function.h"
 #include "rtl.h"
-#include "hash-set.h"
-#include "machmode.h"
-#include "vec.h"
-#include "double-int.h"
-#include "input.h"
-#include "alias.h"
-#include "symtab.h"
-#include "wide-int.h"
-#include "inchash.h"
-#include "real.h"
 #include "tree.h"
+#include "tree-pass.h"
+#include "memmodel.h"
+#include "tm_p.h"
+#include "emit-rtl.h"
 #include "stor-layout.h"
-#include "hard-reg-set.h"
-#include "function.h"
 #include "cfgbuild.h"
-#include "dwarf2.h"
 #include "dwarf2out.h"
 #include "dwarf2asm.h"
-#include "ggc.h"
-#include "hash-table.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 "hashtab.h"
-#include "statistics.h"
-#include "fixed-value.h"
-#include "insn-config.h"
-#include "expmed.h"
-#include "dojump.h"
-#include "explow.h"
-#include "calls.h"
-#include "emit-rtl.h"
-#include "varasm.h"
-#include "stmt.h"
+#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 */
-
+#include "flags.h"             /* dwarf_debuginfo_p */
 
 /* ??? Poison these here until it can be done generically.  They've been
    totally replaced in this file; make sure it stays that way.  */
@@ -79,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
@@ -93,13 +68,19 @@ typedef struct GTY(()) dw_cfi_row_struct
 
   /* The expressions for any register column that is saved.  */
   cfi_vec reg_save;
-} 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;
+};
 
 
 /* Since we no longer have a proper CFG, we're going to create a facsimile
@@ -119,7 +100,7 @@ typedef struct GTY(()) reg_saved_in_data_struct {
    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_insn *head;
@@ -131,8 +112,8 @@ typedef struct
      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.  */
-  HOST_WIDE_INT beg_true_args_size, end_true_args_size;
-  HOST_WIDE_INT beg_delay_args_size, end_delay_args_size;
+  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;
@@ -172,30 +153,28 @@ typedef struct
 
   /* True if we've seen different values incoming to beg_true_args_size.  */
   bool args_size_undefined;
-} dw_trace_info;
 
-
-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 : typed_noop_remove <dw_trace_info>
+struct trace_info_hasher : nofree_ptr_hash <dw_trace_info>
 {
-  typedef dw_trace_info value_type;
-  typedef dw_trace_info compare_type;
-  static inline hashval_t hash (const value_type *);
-  static inline bool equal (const value_type *, const compare_type *);
+  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 value_type *ti)
+trace_info_hasher::hash (const dw_trace_info *ti)
 {
   return INSN_UID (ti->head);
 }
 
 inline bool
-trace_info_hasher::equal (const value_type *a, const compare_type *b)
+trace_info_hasher::equal (const dw_trace_info *a, const dw_trace_info *b)
 {
   return a->head == b->head;
 }
@@ -203,7 +182,7 @@ trace_info_hasher::equal (const value_type *a, const compare_type *b)
 
 /* The variables making up the pseudo-cfg, as described above.  */
 static vec<dw_trace_info> trace_info;
-static vec<dw_trace_info_ref> trace_work_list;
+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.  */
@@ -218,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;
@@ -237,11 +216,11 @@ static dw_cfa_location *cur_cfa;
    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;
+  poly_int64_pod cfa_offset;
+};
 
 
 static vec<queued_reg_save> queued_reg_saves;
@@ -266,7 +245,7 @@ expand_builtin_dwarf_sp_column (void)
    which has mode MODE.  Initialize column C as a return address column.  */
 
 static void
-init_return_column_size (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);
@@ -278,7 +257,7 @@ init_return_column_size (machine_mode mode, rtx mem, unsigned int c)
    init_one_dwarf_reg_size to communicate on what has been done by the
    latter.  */
 
-typedef struct
+struct init_one_dwarf_reg_state
 {
   /* Whether the dwarf return column was initialized.  */
   bool wrote_return_column;
@@ -287,7 +266,7 @@ typedef struct
      was given REGNO to process already.  */
   bool processed_regno [FIRST_PSEUDO_REGISTER];
 
-} init_one_dwarf_reg_state;
+};
 
 /* Helper for expand_builtin_init_dwarf_reg_sizes.  Generate code to
    initialize the dwarf register size table entry corresponding to register
@@ -304,8 +283,8 @@ void init_one_dwarf_reg_size (int regno, machine_mode regmode,
   const unsigned int rnum = DWARF2_FRAME_REG_OUT (dnum, 1);
   const unsigned int dcol = DWARF_REG_TO_UNWIND_COLUMN (rnum);
   
-  const HOST_WIDE_INT slotoffset = dcol * GET_MODE_SIZE (slotmode);
-  const HOST_WIDE_INT regsize = GET_MODE_SIZE (regmode);
+  poly_int64 slotoffset = dcol * GET_MODE_SIZE (slotmode);
+  poly_int64 regsize = GET_MODE_SIZE (regmode);
 
   init_state->processed_regno[regno] = true;
 
@@ -319,7 +298,8 @@ void init_one_dwarf_reg_size (int regno, machine_mode regmode,
       init_state->wrote_return_column = true;
     }
 
-  if (slotoffset < 0)
+  /* ??? 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),
@@ -333,7 +313,7 @@ void
 expand_builtin_init_dwarf_reg_sizes (tree address)
 {
   unsigned int i;
-  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);
 
@@ -468,6 +448,16 @@ copy_cfi_row (dw_cfi_row *src)
   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;
+}
+
 /* Generate a new label for the CFI info to refer to.  */
 
 static char *
@@ -499,16 +489,19 @@ add_cfi (dw_cfi_ref 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 (size >= 0);
+  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);
 }
@@ -531,7 +524,7 @@ static void
 update_row_reg_save (dw_cfi_row *row, unsigned column, dw_cfi_ref cfi)
 {
   if (vec_safe_length (row->reg_save) <= column)
-    vec_safe_grow_cleared (row->reg_save, column + 1);
+    vec_safe_grow_cleared (row->reg_save, column + 1, true);
   (*row->reg_save)[column] = cfi;
 }
 
@@ -663,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:
@@ -688,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.  */
@@ -712,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 ();
 }
@@ -774,6 +772,12 @@ cfi_row_equal_p (dw_cfi_row *a, dw_cfi_row *b)
         return false;
     }
 
+  if (a->window_save != b->window_save)
+    return false;
+
+  if (a->ra_mangled != b->ra_mangled)
+    return false;
+
   return true;
 }
 
@@ -792,42 +796,50 @@ 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;
     }
-  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;
     }
-  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
     {
@@ -839,6 +851,13 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
       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;
@@ -870,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)
-    {
-      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 (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)
     {
@@ -920,18 +948,21 @@ reg_save (unsigned int reg, unsigned int sreg, HOST_WIDE_INT offset)
    and adjust data structures to match.  */
 
 static void
-notice_args_size (rtx insn)
+notice_args_size (rtx_insn *insn)
 {
-  HOST_WIDE_INT args_size, delta;
+  poly_int64 args_size, delta;
   rtx note;
 
   note = find_reg_note (insn, REG_ARGS_SIZE, NULL);
   if (note == NULL)
     return;
 
-  args_size = INTVAL (XEXP (note, 0));
+  if (!cur_trace->eh_head)
+    cur_trace->args_size_defined_for_eh = true;
+
+  args_size = get_args_size (note);
   delta = args_size - cur_trace->end_true_args_size;
-  if (delta == 0)
+  if (known_eq (delta, 0))
     return;
 
   cur_trace->end_true_args_size = args_size;
@@ -944,9 +975,9 @@ notice_args_size (rtx insn)
 
       /* Convert a change in args_size (always a positive in the
         direction of stack growth) to a change in stack pointer.  */
-#ifndef STACK_GROWS_DOWNWARD
-      delta = -delta;
-#endif
+      if (!STACK_GROWS_DOWNWARD)
+       delta = -delta;
+
       cur_cfa->offset += delta;
     }
 }
@@ -957,16 +988,14 @@ notice_args_size (rtx insn)
 static void
 notice_eh_throw (rtx_insn *insn)
 {
-  HOST_WIDE_INT args_size;
-
-  args_size = cur_trace->end_true_args_size;
+  poly_int64 args_size = cur_trace->end_true_args_size;
   if (cur_trace->eh_head == NULL)
     {
       cur_trace->eh_head = insn;
       cur_trace->beg_delay_args_size = args_size;
       cur_trace->end_delay_args_size = args_size;
     }
-  else if (cur_trace->end_delay_args_size != args_size)
+  else if (maybe_ne (cur_trace->end_delay_args_size, args_size))
     {
       cur_trace->end_delay_args_size = args_size;
 
@@ -1029,7 +1058,7 @@ record_reg_saved_in_reg (rtx dest, rtx src)
    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};
@@ -1131,20 +1160,11 @@ dwarf2out_frame_debug_def_cfa (rtx pat)
 {
   memset (cur_cfa, 0, sizeof (*cur_cfa));
 
-  if (GET_CODE (pat) == PLUS)
-    {
-      cur_cfa->offset = INTVAL (XEXP (pat, 1));
-      pat = XEXP (pat, 0);
-    }
+  pat = strip_offset (pat, &cur_cfa->offset);
   if (MEM_P (pat))
     {
       cur_cfa->indirect = 1;
-      pat = XEXP (pat, 0);
-      if (GET_CODE (pat) == PLUS)
-       {
-         cur_cfa->base_offset = INTVAL (XEXP (pat, 1));
-         pat = XEXP (pat, 0);
-       }
+      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.  */
@@ -1167,7 +1187,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat)
     {
     case PLUS:
       gcc_assert (dwf_regno (XEXP (src, 0)) == cur_cfa->reg);
-      cur_cfa->offset -= INTVAL (XEXP (src, 1));
+      cur_cfa->offset -= rtx_to_poly_int64 (XEXP (src, 1));
       break;
 
     case REG:
@@ -1186,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;
 
@@ -1204,7 +1224,7 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
       break;
     case PLUS:
       gcc_assert (dwf_regno (XEXP (addr, 0)) == cur_cfa->reg);
-      offset = INTVAL (XEXP (addr, 1)) - cur_cfa->offset;
+      offset = rtx_to_poly_int64 (XEXP (addr, 1)) - cur_cfa->offset;
       break;
     default:
       gcc_unreachable ();
@@ -1229,7 +1249,7 @@ dwarf2out_frame_debug_cfa_offset (rtx set)
     {
       /* We have a PARALLEL describing where the contents of SRC live.
         Adjust the offset for each piece of the PARALLEL.  */
-      HOST_WIDE_INT span_offset = offset;
+      poly_int64 span_offset = offset;
 
       gcc_assert (GET_CODE (span) == PARALLEL);
 
@@ -1268,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)
@@ -1300,6 +1320,29 @@ 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
@@ -1333,8 +1376,9 @@ dwarf2out_frame_debug_cfa_restore (rtx reg)
 }
 
 /* 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)
@@ -1343,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,
@@ -1546,7 +1606,7 @@ static void
 dwarf2out_frame_debug_expr (rtx expr)
 {
   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
@@ -1635,9 +1695,19 @@ dwarf2out_frame_debug_expr (rtx expr)
              if (fde
                  && fde->stack_realign
                  && REGNO (src) == STACK_POINTER_REGNUM)
-               gcc_assert (REGNO (dest) == HARD_FRAME_POINTER_REGNUM
-                           && fde->drap_reg != INVALID_REGNUM
-                           && cur_cfa->reg != dwf_regno (src));
+               {
+                 gcc_assert (REGNO (dest) == HARD_FRAME_POINTER_REGNUM
+                             && fde->drap_reg != INVALID_REGNUM
+                             && cur_cfa->reg != dwf_regno (src)
+                             && fde->rule18);
+                 fde->rule18 = 0;
+                 /* The save of hard frame pointer has been deferred
+                    until this point when Rule 18 applied.  Emit it now.  */
+                 queue_reg_save (dest, NULL_RTX, 0);
+                 /* And as the instruction modifies the hard frame pointer,
+                    flush the queue as well.  */
+                 dwarf2out_flush_queued_reg_saves ();
+               }
              else
                queue_reg_save (src, dest, 0);
            }
@@ -1650,19 +1720,14 @@ 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)
                {
@@ -1691,9 +1756,8 @@ dwarf2out_frame_debug_expr (rtx expr)
              gcc_assert (frame_pointer_needed);
 
              gcc_assert (REG_P (XEXP (src, 0))
-                         && dwf_regno (XEXP (src, 0)) == cur_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;
              cur_cfa->offset += offset;
@@ -1706,11 +1770,11 @@ dwarf2out_frame_debug_expr (rtx expr)
              /* Rule 4 */
              if (REG_P (XEXP (src, 0))
                  && dwf_regno (XEXP (src, 0)) == cur_cfa->reg
-                 && CONST_INT_P (XEXP (src, 1)))
+                 && 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));
+                 offset = -offset;
                  cur_cfa->offset += offset;
                  cur_cfa->reg = dwf_regno (dest);
                  /* Or used to save regs to the stack.  */
@@ -1733,11 +1797,9 @@ dwarf2out_frame_debug_expr (rtx expr)
 
              /* 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 ();
            }
@@ -1745,8 +1807,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 */
@@ -1756,7 +1819,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,
@@ -1811,9 +1878,7 @@ 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);
@@ -1852,6 +1917,7 @@ dwarf2out_frame_debug_expr (rtx expr)
            {
              gcc_assert (cur_cfa->reg != dw_frame_pointer_regnum);
              cur_trace->cfa_store.offset = 0;
+             fde->rule18 = 1;
            }
 
          if (cur_cfa->reg == dw_stack_pointer_regnum)
@@ -1871,9 +1937,8 @@ 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;
 
@@ -1934,7 +1999,7 @@ dwarf2out_frame_debug_expr (rtx expr)
        {
          /* We're storing the current CFA reg into the stack.  */
 
-         if (cur_cfa->offset == 0)
+         if (known_eq (cur_cfa->offset, 0))
            {
               /* Rule 19 */
               /* If stack is aligned, putting CFA reg into stack means
@@ -1987,12 +2052,22 @@ dwarf2out_frame_debug_expr (rtx expr)
        span = NULL;
 
       if (!span)
-       queue_reg_save (src, NULL_RTX, offset);
+       {
+         if (fde->rule18)
+           /* Just verify the hard frame pointer save when doing dynamic
+              realignment uses expected offset.  The actual queue_reg_save
+              needs to be deferred until the instruction that sets
+              hard frame pointer to stack pointer, see PR99334 for
+              details.  */
+           gcc_assert (known_eq (offset, 0));
+         else
+           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.  */
-         HOST_WIDE_INT span_offset = offset;
+         poly_int64 span_offset = offset;
 
          gcc_assert (GET_CODE (span) == PARALLEL);
 
@@ -2066,10 +2141,16 @@ dwarf2out_frame_debug (rtx_insn *insn)
        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;
 
@@ -2101,6 +2182,11 @@ dwarf2out_frame_debug (rtx_insn *insn)
        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;
@@ -2166,6 +2252,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
@@ -2184,8 +2289,7 @@ cfi_label_required_p (dw_cfi_ref cfi)
 
   if (dwarf_version == 2
       && debug_info_level > DINFO_LEVEL_TERSE
-      && (write_symbols == DWARF2_DEBUG
-         || write_symbols == VMS_AND_DWARF2_DEBUG))
+      && dwarf_debuginfo_p ())
     {
       switch (cfi->dw_cfi_opc)
        {
@@ -2212,20 +2316,13 @@ add_cfis_to_fde (void)
 {
   dw_fde_ref fde = cfun->fde;
   rtx_insn *insn, *next;
-  /* We always start with a function_begin label.  */
-  bool first = false;
 
   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_safe_length (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)
        {
@@ -2247,16 +2344,14 @@ add_cfis_to_fde (void)
              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 (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;
            }
 
@@ -2267,11 +2362,12 @@ add_cfis_to_fde (void)
              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.  */
 
@@ -2279,7 +2375,6 @@ static void
 maybe_record_trace_start (rtx_insn *start, rtx_insn *origin)
 {
   dw_trace_info *ti;
-  HOST_WIDE_INT args_size;
 
   ti = get_trace_info (start);
   gcc_assert (ti != NULL);
@@ -2292,7 +2387,7 @@ maybe_record_trace_start (rtx_insn *start, rtx_insn *origin)
               (origin ? INSN_UID (origin) : 0));
     }
 
-  args_size = cur_trace->end_true_args_size;
+  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
@@ -2315,10 +2410,24 @@ maybe_record_trace_start (rtx_insn *start, rtx_insn *origin)
       /* 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 (ti->beg_true_args_size != args_size)
+      if (maybe_ne (ti->beg_true_args_size, args_size))
        ti->args_size_undefined = true;
     }
 }
@@ -2329,11 +2438,11 @@ maybe_record_trace_start (rtx_insn *start, rtx_insn *origin)
 static void
 maybe_record_trace_start_abnormal (rtx_insn *start, rtx_insn *origin)
 {
-  HOST_WIDE_INT save_args_size, delta;
+  poly_int64 save_args_size, delta;
   dw_cfa_location save_cfa;
 
   save_args_size = cur_trace->end_true_args_size;
-  if (save_args_size == 0)
+  if (known_eq (save_args_size, 0))
     {
       maybe_record_trace_start (start, origin);
       return;
@@ -2347,9 +2456,9 @@ maybe_record_trace_start_abnormal (rtx_insn *start, rtx_insn *origin)
     {
       /* Convert a change in args_size (always a positive in the
         direction of stack growth) to a change in stack pointer.  */
-#ifndef STACK_GROWS_DOWNWARD
-      delta = -delta;
-#endif
+      if (!STACK_GROWS_DOWNWARD)
+       delta = -delta;
+
       cur_row->cfa.offset += delta;
     }
   
@@ -2385,11 +2494,20 @@ create_trace_edges (rtx_insn *insn)
              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 (rtx_insn_list *lab = forced_labels; lab; lab = lab->next ())
-           maybe_record_trace_start (lab->insn (), insn);
+         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))
        ;
@@ -2454,7 +2572,7 @@ scan_insn_after (rtx_insn *insn)
    instructions therein.  */
 
 static void
-scan_trace (dw_trace_info *trace)
+scan_trace (dw_trace_info *trace, bool entry)
 {
   rtx_insn *prev, *insn = trace->head;
   dw_cfa_location this_cfa;
@@ -2473,6 +2591,17 @@ scan_trace (dw_trace_info *trace)
   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))
@@ -2523,7 +2652,6 @@ scan_trace (dw_trace_info *trace)
 
              if (INSN_FROM_TARGET_P (elt))
                {
-                 HOST_WIDE_INT restore_args_size;
                  cfi_vec save_row_reg_save;
 
                  /* If ELT is an instruction from target of an annulled
@@ -2531,7 +2659,7 @@ scan_trace (dw_trace_info *trace)
                     the args_size and CFA along the current path
                     shouldn't change.  */
                  add_cfi_insn = NULL;
-                 restore_args_size = cur_trace->end_true_args_size;
+                 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);
 
@@ -2624,6 +2752,7 @@ scan_trace (dw_trace_info *trace)
       create_trace_edges (control);
     }
 
+  gcc_assert (!cfun->fde || !cfun->fde->rule18);
   add_cfi_insn = NULL;
   cur_row = NULL;
   cur_trace = NULL;
@@ -2642,12 +2771,12 @@ create_cfi_notes (void)
 
   /* Always begin at the entry trace.  */
   ti = &trace_info[0];
-  scan_trace (ti);
+  scan_trace (ti, true);
 
   while (!trace_work_list.is_empty ())
     {
       ti = trace_work_list.pop ();
-      scan_trace (ti);
+      scan_trace (ti, false);
     }
 
   queued_reg_saves.release ();
@@ -2675,7 +2804,7 @@ before_next_cfi_note (rtx_insn *start)
 static void
 connect_traces (void)
 {
-  unsigned i, n = trace_info.length ();
+  unsigned i, n;
   dw_trace_info *prev_ti, *ti;
 
   /* ??? Ideally, we should have both queued and processed every trace.
@@ -2686,20 +2815,15 @@ 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 = &trace_info[i];
-      if (ti->beg_row == NULL)
-       {
-         trace_info.ordered_remove (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.  */
+  n = trace_info.length ();
   prev_ti = &trace_info[n - 1];
   for (i = n - 1; i > 0; --i)
     {
@@ -2745,6 +2869,12 @@ connect_traces (void)
              cfi->dw_cfi_opc = DW_CFA_restore_state;
              add_cfi (cfi);
 
+             /* If the target unwinder does not save the CFA as part of the
+                register state, we need to restore it separately.  */
+             if (targetm.asm_out.should_restore_cfa_state ()
+                 && (cfi = def_cfa_0 (&old_row->cfa, &ti->beg_row->cfa)))
+               add_cfi (cfi);
+
              old_row = prev_ti->beg_row;
            }
          /* Otherwise, we'll simply change state from the previous end.  */
@@ -2773,7 +2903,7 @@ connect_traces (void)
   /* Connect args_size between traces that have can_throw_internal insns.  */
   if (cfun->eh->lp_array)
     {
-      HOST_WIDE_INT prev_args_size = 0;
+      poly_int64 prev_args_size = 0;
 
       for (i = 0; i < n; ++i)
        {
@@ -2781,11 +2911,17 @@ connect_traces (void)
 
          if (ti->switch_sections)
            prev_args_size = 0;
+
          if (ti->eh_head == NULL)
            continue;
-         gcc_assert (!ti->args_size_undefined);
 
-         if (ti->beg_delay_args_size != prev_args_size)
+         /* 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);
@@ -2879,7 +3015,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))
     {
@@ -2900,12 +3036,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:
@@ -2951,7 +3087,12 @@ create_cie_data (void)
   /* On entry, the Canonical Frame Address is at SP.  */
   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
@@ -3047,7 +3188,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);
@@ -3073,7 +3215,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);
@@ -3220,6 +3363,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;
 
@@ -3260,7 +3404,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;
 
@@ -3283,7 +3427,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;
 
@@ -3318,13 +3462,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;
@@ -3334,16 +3478,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);
@@ -3397,6 +3538,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.  */
 
@@ -3404,9 +3556,9 @@ bool
 dwarf2out_do_frame (void)
 {
   /* We want to emit correct CFA location expressions or lists, so we
-     have to return true if we're going to output debug info, even if
+     have to return true if we're going to generate debug info, even if
      we're not going to output frame or unwind info.  */
-  if (write_symbols == DWARF2_DEBUG || write_symbols == VMS_AND_DWARF2_DEBUG)
+  if (dwarf_debuginfo_p () || dwarf_based_debuginfo_p ())
     return true;
 
   if (saved_do_cfi_asm > 0)
@@ -3415,8 +3567,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;
@@ -3451,9 +3602,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!  */
@@ -3492,11 +3641,10 @@ public:
 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.