]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/dwarf2cfi.c
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / dwarf2cfi.c
index f30274e1471b0b4e3044a609f5e15852c95a30ae..df9b625f5bcc255404066dada43f8606776553db 100644 (file)
@@ -1,5 +1,5 @@
 /* Dwarf2 Call Frame Information helper routines.
-   Copyright (C) 1992-2014 Free Software Foundation, Inc.
+   Copyright (C) 1992-2021 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -20,30 +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 "tree.h"
+#include "tree-pass.h"
+#include "memmodel.h"
+#include "tm_p.h"
+#include "emit-rtl.h"
 #include "stor-layout.h"
-#include "function.h"
-#include "basic-block.h"
-#include "dwarf2.h"
+#include "cfgbuild.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 "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.  */
@@ -57,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,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
@@ -97,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;
@@ -109,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;
@@ -150,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;
 }
@@ -181,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.  */
@@ -196,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;
@@ -215,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;
@@ -244,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 (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);
@@ -252,46 +253,104 @@ init_return_column_size (enum machine_mode mode, rtx mem, unsigned int c)
                  gen_int_mode (size, mode));
 }
 
-/* Generate code to initialize the register size table.  */
+/* 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 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;
+
+      save_mode = targetm.dwarf_frame_reg_mode (i);
+      span = targetm.dwarf_register_span (gen_rtx_REG (save_mode, i));
 
-         if (HARD_REGNO_CALL_PART_CLOBBERED (i, save_mode))
-           save_mode = choose_hard_reg_mode (i, 1, true);
-         if (dnum == DWARF_FRAME_RETURN_COLUMN)
+      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
@@ -389,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 *
@@ -420,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);
 }
@@ -452,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;
 }
 
@@ -584,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:
@@ -609,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.  */
@@ -633,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 ();
 }
@@ -695,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;
 }
 
@@ -713,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
     {
@@ -760,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;
@@ -791,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)
     {
@@ -841,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;
@@ -865,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;
     }
 }
@@ -878,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;
 
@@ -950,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};
@@ -1052,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.  */
@@ -1088,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:
@@ -1107,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;
 
@@ -1125,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 ();
@@ -1150,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);
 
@@ -1189,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)
@@ -1221,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
@@ -1254,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)
@@ -1264,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,
@@ -1467,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
@@ -1556,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);
            }
@@ -1571,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)
                {
@@ -1612,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;
@@ -1627,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.  */
@@ -1654,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 ();
            }
@@ -1666,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 */
@@ -1677,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,
@@ -1732,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);
@@ -1773,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)
@@ -1792,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;
 
@@ -1855,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
@@ -1908,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);
 
@@ -1937,16 +2091,16 @@ dwarf2out_frame_debug_expr (rtx expr)
    register to the stack.  */
 
 static void
-dwarf2out_frame_debug (rtx insn)
+dwarf2out_frame_debug (rtx_insn *insn)
 {
-  rtx note, n;
+  rtx note, n, pat;
   bool handled_one = 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:
@@ -1987,10 +2141,16 @@ dwarf2out_frame_debug (rtx 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;
 
@@ -2022,6 +2182,11 @@ dwarf2out_frame_debug (rtx 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;
@@ -2038,14 +2203,14 @@ dwarf2out_frame_debug (rtx insn)
 
   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 (clobbers_queued_reg_save (insn))
+      if (clobbers_queued_reg_save (pat))
        dwarf2out_flush_queued_reg_saves ();
     }
 }
@@ -2087,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
@@ -2105,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)
        {
@@ -2133,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)
        {
@@ -2168,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;
            }
 
@@ -2188,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.  */
 
@@ -2200,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);
@@ -2213,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
@@ -2236,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;
     }
 }
@@ -2250,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;
@@ -2268,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;
     }
   
@@ -2306,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))
        ;
@@ -2364,7 +2561,7 @@ create_trace_edges (rtx_insn *insn)
 /* A subroutine of scan_trace.  Do what needs to be done "after" INSN.  */
 
 static void
-scan_insn_after (rtx insn)
+scan_insn_after (rtx_insn *insn)
 {
   if (RTX_FRAME_RELATED_P (insn))
     dwarf2out_frame_debug (insn);
@@ -2375,7 +2572,7 @@ scan_insn_after (rtx 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;
@@ -2394,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))
@@ -2425,7 +2633,7 @@ scan_trace (dw_trace_info *trace)
         handling for the positioning of the notes.  */
       if (rtx_sequence *pat = dyn_cast <rtx_sequence *> (PATTERN (insn)))
        {
-         rtx elt;
+         rtx_insn *elt;
          int i, n = pat->len ();
 
          control = pat->insn (0);
@@ -2440,11 +2648,10 @@ scan_trace (dw_trace_info *trace)
              gcc_assert (!RTX_FRAME_RELATED_P (control));
              gcc_assert (!find_reg_note (control, REG_ARGS_SIZE, NULL));
 
-             elt = pat->element (1);
+             elt = pat->insn (1);
 
              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
@@ -2452,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);
 
@@ -2495,7 +2702,7 @@ scan_trace (dw_trace_info *trace)
 
          for (i = 1; i < n; ++i)
            {
-             elt = pat->element (i);
+             elt = pat->insn (i);
              scan_insn_after (elt);
            }
 
@@ -2545,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;
@@ -2563,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 ();
@@ -2596,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.
@@ -2607,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)
     {
@@ -2666,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.  */
@@ -2694,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)
        {
@@ -2702,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);
@@ -2765,7 +2980,7 @@ create_pseudo_cfg (void)
          memset (&ti, 0, sizeof (ti));
          ti.head = insn;
          ti.switch_sections = switch_sections;
-         ti.id = trace_info.length () - 1;
+         ti.id = trace_info.length ();
          trace_info.safe_push (ti);
 
          saw_barrier = false;
@@ -2783,7 +2998,7 @@ create_pseudo_cfg (void)
       dw_trace_info **slot;
 
       if (dump_file)
-       fprintf (dump_file, "Creating trace %u : start at %s %d%s\n", i,
+       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)" : "");
 
@@ -2800,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))
     {
@@ -2821,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:
@@ -2862,7 +3077,6 @@ 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));
   cur_trace = &cie_trace;
@@ -2873,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
@@ -2915,6 +3134,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 ();
@@ -2966,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);
@@ -2992,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);
@@ -3139,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;
 
@@ -3179,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;
 
@@ -3202,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;
 
@@ -3237,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;
@@ -3253,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);
@@ -3316,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.  */
 
@@ -3323,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)
@@ -3334,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;
@@ -3370,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!  */
@@ -3411,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.