diff mbox

[3/6] Allow jumps in epilogues

Message ID 4D94DD06.9030507@codesourcery.com
State New
Headers show

Commit Message

Bernd Schmidt March 31, 2011, 7:59 p.m. UTC
On 03/26/2011 04:26 AM, Richard Henderson wrote:
> I think the ideal thing would be a pass while the cfg is still extant that
> captures the unwind info into notes; these can be recorded at basic block
> boundaries, so that they persist until the end of compilation.
> 
> So long as late late compilation passes continue to not move frame-related
> insns across basic block boundaries, we should be fine.

I'm nervous about this as the reorg pass can do arbitrary
transformations. On Blackfin for example, we can reorder basic blocks
for the sake of loop optimizations; sched-ebb can create new blocks,
etc. I think it would be best if we can somehow make it work during
final, without a CFG.

> I'm willing to work with you on the problem of cfg-aware unwind info.  We
> have needed this for a really long time; there are existing bugs related 
> to exception handling and !ACCUMULATE_OUTGOING_ARGS that would be fixed by
> this.

I'm appending a series of draft patches. It tries to compute CFIs for
stretches of straight-line code and records state for potential jump
targets, iterating until everything is covered. Then, a pass through all
insns from start to finish reassembles the pieces into a coherent string
of CFI insns.

Rather than use a CFG, I've tried to do something similar to
compute_barrier_args_size, using JUMP_LABELs etc.

Summary of the patches:
001 - just create a dwarf2out_frame_debug_init function.
002 - Make it walk the function in a first pass and record CFIs to
      be output later
003 - Store dw_cfi_refs in VECs rather than linked lists. Looks
      larger than it is due to reindentation
004 - Change the function walk introduced in 002 so that it records
      and restores state when reaching jumps/barriers

For now I'd just like some input on whether this looks remotely viable.
There are a number of known problems with it:
* The generated CFIs are inefficient (poor use of remember/restore)
* -freorder-blocks-and-partition is broken
* i386.c uses dwarf2out_frame_debug directly in some cases and is
  unconverted
* I haven't tested whether my attempt to use
  get_eh_landing_pad_from_rtx in the absence of a CFG actually works
* Computed jumps and nonlocal gotos aren't handled. I think this
  could be done by recording the state at NOTE_INSN_PROLOGUE_END
  and using that for all labels we can't otherwise reach.


Bernd

* cfgcleanup.c (flow_find_head_matching_sequence): Ignore
    	epilogue notes.
    	* df-problems.c (can_move_insns_across): Don't stop at epilogue
    	notes.
    	* dwarf2out.c (dwarf2out_cfi_begin_epilogue): Also allow a
    	simplejump to end the block.

Index: gcc/dwarf2out.c
===================================================================
--- gcc.orig/dwarf2out.c
+++ gcc/dwarf2out.c
@@ -471,6 +471,8 @@ static void output_call_frame_info (int)
 static void dwarf2out_note_section_used (void);
 static bool clobbers_queued_reg_save (const_rtx);
 static void dwarf2out_frame_debug_expr (rtx, const char *);
+static void dwarf2out_cfi_begin_epilogue (rtx);
+static void dwarf2out_frame_debug_restore_state (void);
 
 /* Support for complex CFA locations.  */
 static void output_cfa_loc (dw_cfi_ref, int);
@@ -879,6 +881,9 @@ dwarf2out_cfi_label (bool force)
   return label;
 }
 
+/* The insn after which a new CFI note should be emitted.  */
+static rtx cfi_insn;
+
 /* True if remember_state should be emitted before following CFI directive.  */
 static bool emit_cfa_remember;
 
@@ -961,7 +966,8 @@ add_fde_cfi (const char *label, dw_cfi_r
 	        }
 	    }
 
-	  output_cfi_directive (cfi);
+	  cfi_insn = emit_note_after (NOTE_INSN_CFI, cfi_insn);
+	  NOTE_CFI (cfi_insn) = cfi;
 
 	  list_head = &fde->dw_fde_cfi;
 	  any_cfis_emitted = true;
@@ -2790,6 +2796,11 @@ dwarf2out_frame_debug (rtx insn, bool af
   rtx note, n;
   bool handled_one = false;
 
+  if (after_p)
+    cfi_insn = insn;
+  else
+    cfi_insn = PREV_INSN (insn);
+
   if (!NONJUMP_INSN_P (insn) || clobbers_queued_reg_save (insn))
     dwarf2out_flush_queued_reg_saves ();
 
@@ -2911,6 +2922,7 @@ void
 dwarf2out_frame_debug_init (void)
 {
   size_t i;
+  rtx insn;
 
   /* Flush any queued register saves.  */
   dwarf2out_flush_queued_reg_saves ();
@@ -2937,12 +2949,64 @@ dwarf2out_frame_debug_init (void)
       XDELETEVEC (barrier_args_size);
       barrier_args_size = NULL;
     }
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      rtx pat;
+      if (BARRIER_P (insn))
+	{
+	  dwarf2out_frame_debug (insn, false);
+	  continue;
+	}
+      else if (NOTE_P (insn))
+	{
+	  switch (NOTE_KIND (insn))
+	    {
+	    case NOTE_INSN_EPILOGUE_BEG:
+#if defined (HAVE_epilogue)
+	      dwarf2out_cfi_begin_epilogue (insn);
+#endif
+	      break;
+	    case NOTE_INSN_CFA_RESTORE_STATE:
+	      cfi_insn = insn;
+	      dwarf2out_frame_debug_restore_state ();
+	      break;
+	    }
+	  continue;
+	}
+      if (!NONDEBUG_INSN_P (insn))
+	continue;
+      pat = PATTERN (insn);
+      if (asm_noperands (pat) >= 0)
+	continue;
+      if (GET_CODE (pat) == SEQUENCE)
+	{
+	  int j;
+	  for (j = 1; j < XVECLEN (pat, 0); j++)
+	    dwarf2out_frame_debug (XVECEXP (pat, 0, j), false);
+	  insn = XVECEXP (pat, 0, 0);
+	}
+
+      if (CALL_P (insn) && dwarf2out_do_frame ())
+	dwarf2out_frame_debug (insn, false);
+      if (dwarf2out_do_frame ()
+#if !defined (HAVE_prologue)
+	  && !ACCUMULATE_OUTGOING_ARGS 
+#endif
+	  )
+	dwarf2out_frame_debug (insn, true);
+    }
+}
+
+void
+dwarf2out_emit_cfi (dw_cfi_ref cfi)
+{
+  output_cfi_directive (cfi);
 }
 
-/* Determine if we need to save and restore CFI information around this
-   epilogue.  If SIBCALL is true, then this is a sibcall epilogue.  If
-   we do need to save/restore, then emit the save now, and insert a
-   NOTE_INSN_CFA_RESTORE_STATE at the appropriate place in the stream.  */
+/* Determine if we need to save and restore CFI information around
+   this epilogue.  If we do need to save/restore, then emit the save
+   now, and insert a NOTE_INSN_CFA_RESTORE_STATE at the appropriate
+   place in the stream.  */
 
 void
 dwarf2out_cfi_begin_epilogue (rtx insn)
@@ -2957,8 +3021,10 @@ dwarf2out_cfi_begin_epilogue (rtx insn)
       if (!INSN_P (i))
 	continue;
 
-      /* Look for both regular and sibcalls to end the block.  */
-      if (returnjump_p (i))
+      /* Look for both regular and sibcalls to end the block.  Various
+	 optimization passes may cause us to jump to a common epilogue
+	 tail, so we also accept simplejumps.  */
+      if (returnjump_p (i) || simplejump_p (i))
 	break;
       if (CALL_P (i) && SIBLING_CALL_P (i))
 	break;
Index: gcc/dwarf2out.h
===================================================================
--- gcc.orig/dwarf2out.h
+++ gcc/dwarf2out.h
@@ -18,11 +18,11 @@ You should have received a copy of the G
 along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
 
+struct dw_cfi_struct;
 extern void dwarf2out_decl (tree);
 extern void dwarf2out_frame_debug (rtx, bool);
 extern void dwarf2out_frame_debug_init (void);
-extern void dwarf2out_cfi_begin_epilogue (rtx);
-extern void dwarf2out_frame_debug_restore_state (void);
+extern void dwarf2out_emit_cfi (struct dw_cfi_struct *);
 extern void dwarf2out_flush_queued_reg_saves (void);
 
 extern void debug_dwarf (void);
Index: gcc/insn-notes.def
===================================================================
--- gcc.orig/insn-notes.def
+++ gcc/insn-notes.def
@@ -77,4 +77,8 @@ INSN_NOTE (SWITCH_TEXT_SECTIONS)
    when an epilogue appears in the middle of a function.  */
 INSN_NOTE (CFA_RESTORE_STATE)
 
+/* When emitting dwarf2 frame information, contains a directive that
+   should be emitted.  */
+INSN_NOTE (CFI)
+
 #undef INSN_NOTE
Index: gcc/rtl.h
===================================================================
--- gcc.orig/rtl.h
+++ gcc/rtl.h
@@ -180,6 +180,7 @@ union rtunion_def
   mem_attrs *rt_mem;
   reg_attrs *rt_reg;
   struct constant_descriptor_rtx *rt_constant;
+  struct dw_cfi_struct *rt_cfi;
 };
 typedef union rtunion_def rtunion;
 
@@ -708,6 +709,7 @@ extern void rtl_check_failed_flag (const
 #define XTREE(RTX, N)   (RTL_CHECK1 (RTX, N, 't').rt_tree)
 #define XBBDEF(RTX, N)	(RTL_CHECK1 (RTX, N, 'B').rt_bb)
 #define XTMPL(RTX, N)	(RTL_CHECK1 (RTX, N, 'T').rt_str)
+#define XCFI(RTX, N)	(RTL_CHECK1 (RTX, N, 'C').rt_cfi)
 
 #define XVECEXP(RTX, N, M)	RTVEC_ELT (XVEC (RTX, N), M)
 #define XVECLEN(RTX, N)		GET_NUM_ELEM (XVEC (RTX, N))
@@ -740,6 +742,7 @@ extern void rtl_check_failed_flag (const
 #define XCMODE(RTX, N, C)     (RTL_CHECKC1 (RTX, N, C).rt_type)
 #define XCTREE(RTX, N, C)     (RTL_CHECKC1 (RTX, N, C).rt_tree)
 #define XCBBDEF(RTX, N, C)    (RTL_CHECKC1 (RTX, N, C).rt_bb)
+#define XCCFI(RTX, N, C)      (RTL_CHECKC1 (RTX, N, C).rt_cfi)
 #define XCCSELIB(RTX, N, C)   (RTL_CHECKC1 (RTX, N, C).rt_cselib)
 
 #define XCVECEXP(RTX, N, M, C)	RTVEC_ELT (XCVEC (RTX, N, C), M)
@@ -882,6 +885,7 @@ extern const char * const reg_note_name[
 #define NOTE_BLOCK(INSN)	XCTREE (INSN, 4, NOTE)
 #define NOTE_EH_HANDLER(INSN)	XCINT (INSN, 4, NOTE)
 #define NOTE_BASIC_BLOCK(INSN)	XCBBDEF (INSN, 4, NOTE)
+#define NOTE_CFI(INSN)		XCCFI (INSN, 4, NOTE)
 #define NOTE_VAR_LOCATION(INSN)	XCEXP (INSN, 4, NOTE)
 
 /* In a NOTE that is a line number, this is the line number.
Index: gcc/dwarf2out.c
===================================================================
--- gcc.orig/dwarf2out.c
+++ gcc/dwarf2out.c
@@ -267,7 +267,6 @@ typedef union GTY(()) dw_cfi_oprnd_struc
 dw_cfi_oprnd;
 
 typedef struct GTY(()) dw_cfi_struct {
-  dw_cfi_ref dw_cfi_next;
   enum dwarf_call_frame_info dw_cfi_opc;
   dw_cfi_oprnd GTY ((desc ("dw_cfi_oprnd1_desc (%1.dw_cfi_opc)")))
     dw_cfi_oprnd1;
@@ -276,6 +275,12 @@ typedef struct GTY(()) dw_cfi_struct {
 }
 dw_cfi_node;
 
+DEF_VEC_P (dw_cfi_ref);
+DEF_VEC_ALLOC_P (dw_cfi_ref, heap);
+DEF_VEC_ALLOC_P (dw_cfi_ref, gc);
+
+typedef VEC(dw_cfi_ref, gc) *cfi_vec;
+
 /* This is how we define the location of the CFA. We use to handle it
    as REG + OFFSET all the time,  but now it can be more complex.
    It can now be either REG + CFA_OFFSET or *(REG + BASE_OFFSET) + CFA_OFFSET.
@@ -306,8 +311,8 @@ typedef struct GTY(()) dw_fde_struct {
   const char *dw_fde_hot_section_end_label;
   const char *dw_fde_unlikely_section_label;
   const char *dw_fde_unlikely_section_end_label;
-  dw_cfi_ref dw_fde_cfi;
-  dw_cfi_ref dw_fde_switch_cfi; /* Last CFI before switching sections.  */
+  cfi_vec dw_fde_cfi;
+  int dw_fde_switch_cfi_index; /* Last CFI before switching sections.  */
   HOST_WIDE_INT stack_realignment;
   unsigned funcdef_number;
   /* Dynamic realign argument pointer register.  */
@@ -416,8 +421,8 @@ current_fde (void)
   return fde_table_in_use ? &fde_table[fde_table_in_use - 1] : NULL;
 }
 
-/* A list of call frame insns for the CIE.  */
-static GTY(()) dw_cfi_ref cie_cfi_head;
+/* A vector of call frame insns for the CIE.  */
+static GTY(()) cfi_vec cie_cfi_vec;
 
 /* Some DWARF extensions (e.g., MIPS/SGI) implement a subprogram
    attribute that accelerates the lookup of the FDE associated
@@ -457,7 +462,7 @@ static GTY(()) section *cold_text_sectio
 static char *stripattributes (const char *);
 static const char *dwarf_cfi_name (unsigned);
 static dw_cfi_ref new_cfi (void);
-static void add_cfi (dw_cfi_ref *, dw_cfi_ref);
+static void add_cfi (cfi_vec *, dw_cfi_ref);
 static void add_fde_cfi (const char *, dw_cfi_ref);
 static void lookup_cfa_1 (dw_cfi_ref, dw_cfa_location *, dw_cfa_location *);
 static void lookup_cfa (dw_cfa_location *);
@@ -815,7 +820,6 @@ new_cfi (void)
 {
   dw_cfi_ref cfi = ggc_alloc_dw_cfi_node ();
 
-  cfi->dw_cfi_next = NULL;
   cfi->dw_cfi_oprnd1.dw_cfi_reg_num = 0;
   cfi->dw_cfi_oprnd2.dw_cfi_reg_num = 0;
 
@@ -825,9 +829,8 @@ new_cfi (void)
 /* Add a Call Frame Instruction to list of instructions.  */
 
 static inline void
-add_cfi (dw_cfi_ref *list_head, dw_cfi_ref cfi)
+add_cfi (cfi_vec *vec, dw_cfi_ref cfi)
 {
-  dw_cfi_ref *p;
   dw_fde_ref fde = current_fde ();
 
   /* When DRAP is used, CFA is defined with an expression.  Redefine
@@ -849,11 +852,7 @@ add_cfi (dw_cfi_ref *list_head, dw_cfi_r
           break;
       }
 
-  /* Find the end of the chain.  */
-  for (p = list_head; (*p) != NULL; p = &(*p)->dw_cfi_next)
-    ;
-
-  *p = cfi;
+  VEC_safe_push (dw_cfi_ref, gc, *vec, cfi);
 }
 
 /* Generate a new label for the CFI info to refer to.  FORCE is true
@@ -896,7 +895,12 @@ static bool any_cfis_emitted;
 static void
 add_fde_cfi (const char *label, dw_cfi_ref cfi)
 {
-  dw_cfi_ref *list_head;
+  cfi_vec *vec;
+
+  if (cie_cfi_vec == NULL)
+    cie_cfi_vec = VEC_alloc (dw_cfi_ref, gc, 20);
+
+  vec = &cie_cfi_vec;
 
   if (emit_cfa_remember)
     {
@@ -909,8 +913,6 @@ add_fde_cfi (const char *label, dw_cfi_r
       add_fde_cfi (label, cfi_remember);
     }
 
-  list_head = &cie_cfi_head;
-
   if (dwarf2out_do_cfi_asm ())
     {
       if (label)
@@ -969,7 +971,7 @@ add_fde_cfi (const char *label, dw_cfi_r
 	  cfi_insn = emit_note_after (NOTE_INSN_CFI, cfi_insn);
 	  NOTE_CFI (cfi_insn) = cfi;
 
-	  list_head = &fde->dw_fde_cfi;
+	  vec = &fde->dw_fde_cfi;
 	  any_cfis_emitted = true;
 	}
       /* ??? If this is a CFI for the CIE, we don't emit.  This
@@ -1007,11 +1009,11 @@ add_fde_cfi (const char *label, dw_cfi_r
 	  fde->dw_fde_current_label = label;
 	}
 
-      list_head = &fde->dw_fde_cfi;
+      vec = &fde->dw_fde_cfi;
       any_cfis_emitted = true;
     }
 
-  add_cfi (list_head, cfi);
+  add_cfi (vec, cfi);
 }
 
 /* Subroutine of lookup_cfa.  */
@@ -1058,6 +1060,7 @@ lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_loc
 static void
 lookup_cfa (dw_cfa_location *loc)
 {
+  int ix;
   dw_cfi_ref cfi;
   dw_fde_ref fde;
   dw_cfa_location remember;
@@ -1066,12 +1069,12 @@ lookup_cfa (dw_cfa_location *loc)
   loc->reg = INVALID_REGNUM;
   remember = *loc;
 
-  for (cfi = cie_cfi_head; cfi; cfi = cfi->dw_cfi_next)
+  FOR_EACH_VEC_ELT (dw_cfi_ref, cie_cfi_vec, ix, cfi)
     lookup_cfa_1 (cfi, loc, &remember);
 
   fde = current_fde ();
   if (fde)
-    for (cfi = fde->dw_fde_cfi; cfi; cfi = cfi->dw_cfi_next)
+    FOR_EACH_VEC_ELT (dw_cfi_ref, fde->dw_fde_cfi, ix, cfi)
       lookup_cfa_1 (cfi, loc, &remember);
 }
 
@@ -3496,169 +3499,181 @@ output_cfi_directive (dw_cfi_ref cfi)
     }
 }
 
-DEF_VEC_P (dw_cfi_ref);
-DEF_VEC_ALLOC_P (dw_cfi_ref, heap);
-
-/* Output CFIs to bring current FDE to the same state as after executing
-   CFIs in CFI chain.  DO_CFI_ASM is true if .cfi_* directives shall
-   be emitted, false otherwise.  If it is false, FDE and FOR_EH are the
-   other arguments to pass to output_cfi.  */
+/* Output CFIs from VEC, up to index UPTO, to bring current FDE to the
+   same state as after executing CFIs in CFI chain.  DO_CFI_ASM is
+   true if .cfi_* directives shall be emitted, false otherwise.  If it
+   is false, FDE and FOR_EH are the other arguments to pass to
+   output_cfi.  */
 
 static void
-output_cfis (dw_cfi_ref cfi, bool do_cfi_asm, dw_fde_ref fde, bool for_eh)
+output_cfis (cfi_vec vec, int upto, bool do_cfi_asm,
+	     dw_fde_ref fde, bool for_eh)
 {
+  int ix;
   struct dw_cfi_struct cfi_buf;
   dw_cfi_ref cfi2;
   dw_cfi_ref cfi_args_size = NULL, cfi_cfa = NULL, cfi_cfa_offset = NULL;
-  VEC (dw_cfi_ref, heap) *regs = VEC_alloc (dw_cfi_ref, heap, 32);
+  VEC(dw_cfi_ref, heap) *regs = VEC_alloc (dw_cfi_ref, heap, 32);
   unsigned int len, idx;
 
-  for (;; cfi = cfi->dw_cfi_next)
-    switch (cfi ? cfi->dw_cfi_opc : DW_CFA_nop)
-      {
-      case DW_CFA_advance_loc:
-      case DW_CFA_advance_loc1:
-      case DW_CFA_advance_loc2:
-      case DW_CFA_advance_loc4:
-      case DW_CFA_MIPS_advance_loc8:
-      case DW_CFA_set_loc:
-	/* All advances should be ignored.  */
-	break;
-      case DW_CFA_remember_state:
+  for (ix = 0; ix < upto + 1; ix++)
+    {
+      dw_cfi_ref cfi = ix < upto ? VEC_index (dw_cfi_ref, vec, ix) : NULL;
+      switch (cfi ? cfi->dw_cfi_opc : DW_CFA_nop)
 	{
-	  dw_cfi_ref args_size = cfi_args_size;
-
-	  /* Skip everything between .cfi_remember_state and
-	     .cfi_restore_state.  */
-	  for (cfi2 = cfi->dw_cfi_next; cfi2; cfi2 = cfi2->dw_cfi_next)
-	    if (cfi2->dw_cfi_opc == DW_CFA_restore_state)
-	      break;
-	    else if (cfi2->dw_cfi_opc == DW_CFA_GNU_args_size)
-	      args_size = cfi2;
-	    else
-	      gcc_assert (cfi2->dw_cfi_opc != DW_CFA_remember_state);
-
-	  if (cfi2 == NULL)
-	    goto flush_all;
-	  else
-	    {
-	      cfi = cfi2;
-	      cfi_args_size = args_size;
-	    }
+	case DW_CFA_advance_loc:
+	case DW_CFA_advance_loc1:
+	case DW_CFA_advance_loc2:
+	case DW_CFA_advance_loc4:
+	case DW_CFA_MIPS_advance_loc8:
+	case DW_CFA_set_loc:
+	  /* All advances should be ignored.  */
 	  break;
-	}
-      case DW_CFA_GNU_args_size:
-	cfi_args_size = cfi;
-	break;
-      case DW_CFA_GNU_window_save:
-	goto flush_all;
-      case DW_CFA_offset:
-      case DW_CFA_offset_extended:
-      case DW_CFA_offset_extended_sf:
-      case DW_CFA_restore:
-      case DW_CFA_restore_extended:
-      case DW_CFA_undefined:
-      case DW_CFA_same_value:
-      case DW_CFA_register:
-      case DW_CFA_val_offset:
-      case DW_CFA_val_offset_sf:
-      case DW_CFA_expression:
-      case DW_CFA_val_expression:
-      case DW_CFA_GNU_negative_offset_extended:
-	if (VEC_length (dw_cfi_ref, regs) <= cfi->dw_cfi_oprnd1.dw_cfi_reg_num)
-	  VEC_safe_grow_cleared (dw_cfi_ref, heap, regs,
-				 cfi->dw_cfi_oprnd1.dw_cfi_reg_num + 1);
-	VEC_replace (dw_cfi_ref, regs, cfi->dw_cfi_oprnd1.dw_cfi_reg_num, cfi);
-	break;
-      case DW_CFA_def_cfa:
-      case DW_CFA_def_cfa_sf:
-      case DW_CFA_def_cfa_expression:
-	cfi_cfa = cfi;
-	cfi_cfa_offset = cfi;
-	break;
-      case DW_CFA_def_cfa_register:
-	cfi_cfa = cfi;
-	break;
-      case DW_CFA_def_cfa_offset:
-      case DW_CFA_def_cfa_offset_sf:
-	cfi_cfa_offset = cfi;
-	break;
-      case DW_CFA_nop:
-	gcc_assert (cfi == NULL);
-      flush_all:
-	len = VEC_length (dw_cfi_ref, regs);
-	for (idx = 0; idx < len; idx++)
+	case DW_CFA_remember_state:
 	  {
-	    cfi2 = VEC_replace (dw_cfi_ref, regs, idx, NULL);
-	    if (cfi2 != NULL
-		&& cfi2->dw_cfi_opc != DW_CFA_restore
-		&& cfi2->dw_cfi_opc != DW_CFA_restore_extended)
+	    dw_cfi_ref args_size = cfi_args_size;
+
+	    /* Skip everything between .cfi_remember_state and
+	       .cfi_restore_state.  */
+	    for (; ix < upto; ix++)
 	      {
-		if (do_cfi_asm)
-		  output_cfi_directive (cfi2);
+		cfi2 = VEC_index (dw_cfi_ref, vec, ix);
+		if (cfi2->dw_cfi_opc == DW_CFA_restore_state)
+		  break;
+		else if (cfi2->dw_cfi_opc == DW_CFA_GNU_args_size)
+		  args_size = cfi2;
 		else
-		  output_cfi (cfi2, fde, for_eh);
+		  gcc_assert (cfi2->dw_cfi_opc != DW_CFA_remember_state);
 	      }
+
+	    if (cfi2 == NULL)
+	      goto flush_all;
+	    cfi_args_size = args_size;
+	    break;
 	  }
-	if (cfi_cfa && cfi_cfa_offset && cfi_cfa_offset != cfi_cfa)
-	  {
-	    gcc_assert (cfi_cfa->dw_cfi_opc != DW_CFA_def_cfa_expression);
-	    cfi_buf = *cfi_cfa;
-	    switch (cfi_cfa_offset->dw_cfi_opc)
-	      {
-	      case DW_CFA_def_cfa_offset:
-		cfi_buf.dw_cfi_opc = DW_CFA_def_cfa;
-		cfi_buf.dw_cfi_oprnd2 = cfi_cfa_offset->dw_cfi_oprnd1;
-		break;
-	      case DW_CFA_def_cfa_offset_sf:
-		cfi_buf.dw_cfi_opc = DW_CFA_def_cfa_sf;
-		cfi_buf.dw_cfi_oprnd2 = cfi_cfa_offset->dw_cfi_oprnd1;
-		break;
-	      case DW_CFA_def_cfa:
-	      case DW_CFA_def_cfa_sf:
-		cfi_buf.dw_cfi_opc = cfi_cfa_offset->dw_cfi_opc;
-		cfi_buf.dw_cfi_oprnd2 = cfi_cfa_offset->dw_cfi_oprnd2;
-		break;
-	      default:
-		gcc_unreachable ();
-	      }
-	    cfi_cfa = &cfi_buf;
-	  }
-	else if (cfi_cfa_offset)
-	  cfi_cfa = cfi_cfa_offset;
-	if (cfi_cfa)
-	  {
-	    if (do_cfi_asm)
-	      output_cfi_directive (cfi_cfa);
-	    else
-	      output_cfi (cfi_cfa, fde, for_eh);
-	  }
-	cfi_cfa = NULL;
-	cfi_cfa_offset = NULL;
-	if (cfi_args_size
-	    && cfi_args_size->dw_cfi_oprnd1.dw_cfi_offset)
-	  {
-	    if (do_cfi_asm)
-	      output_cfi_directive (cfi_args_size);
-	    else
-	      output_cfi (cfi_args_size, fde, for_eh);
-	  }
-	cfi_args_size = NULL;
-	if (cfi == NULL)
-	  {
-	    VEC_free (dw_cfi_ref, heap, regs);
-	    return;
-	  }
-	else if (do_cfi_asm)
-	  output_cfi_directive (cfi);
-	else
-	  output_cfi (cfi, fde, for_eh);
-	break;
-      default:
-	gcc_unreachable ();
+	case DW_CFA_GNU_args_size:
+	  cfi_args_size = cfi;
+	  break;
+	case DW_CFA_GNU_window_save:
+	  goto flush_all;
+	case DW_CFA_offset:
+	case DW_CFA_offset_extended:
+	case DW_CFA_offset_extended_sf:
+	case DW_CFA_restore:
+	case DW_CFA_restore_extended:
+	case DW_CFA_undefined:
+	case DW_CFA_same_value:
+	case DW_CFA_register:
+	case DW_CFA_val_offset:
+	case DW_CFA_val_offset_sf:
+	case DW_CFA_expression:
+	case DW_CFA_val_expression:
+	case DW_CFA_GNU_negative_offset_extended:
+	  if (VEC_length (dw_cfi_ref, regs)
+	      <= cfi->dw_cfi_oprnd1.dw_cfi_reg_num)
+	    VEC_safe_grow_cleared (dw_cfi_ref, heap, regs,
+				   cfi->dw_cfi_oprnd1.dw_cfi_reg_num + 1);
+	  VEC_replace (dw_cfi_ref, regs, cfi->dw_cfi_oprnd1.dw_cfi_reg_num,
+		       cfi);
+	  break;
+	case DW_CFA_def_cfa:
+	case DW_CFA_def_cfa_sf:
+	case DW_CFA_def_cfa_expression:
+	  cfi_cfa = cfi;
+	  cfi_cfa_offset = cfi;
+	  break;
+	case DW_CFA_def_cfa_register:
+	  cfi_cfa = cfi;
+	  break;
+	case DW_CFA_def_cfa_offset:
+	case DW_CFA_def_cfa_offset_sf:
+	  cfi_cfa_offset = cfi;
+	  break;
+	case DW_CFA_nop:
+	  gcc_assert (cfi == NULL);
+	flush_all:
+	  len = VEC_length (dw_cfi_ref, regs);
+	  for (idx = 0; idx < len; idx++)
+	    {
+	      cfi2 = VEC_replace (dw_cfi_ref, regs, idx, NULL);
+	      if (cfi2 != NULL
+		  && cfi2->dw_cfi_opc != DW_CFA_restore
+		  && cfi2->dw_cfi_opc != DW_CFA_restore_extended)
+		{
+		  if (do_cfi_asm)
+		    output_cfi_directive (cfi2);
+		  else
+		    output_cfi (cfi2, fde, for_eh);
+		}
+	    }
+	  if (cfi_cfa && cfi_cfa_offset && cfi_cfa_offset != cfi_cfa)
+	    {
+	      gcc_assert (cfi_cfa->dw_cfi_opc != DW_CFA_def_cfa_expression);
+	      cfi_buf = *cfi_cfa;
+	      switch (cfi_cfa_offset->dw_cfi_opc)
+		{
+		case DW_CFA_def_cfa_offset:
+		  cfi_buf.dw_cfi_opc = DW_CFA_def_cfa;
+		  cfi_buf.dw_cfi_oprnd2 = cfi_cfa_offset->dw_cfi_oprnd1;
+		  break;
+		case DW_CFA_def_cfa_offset_sf:
+		  cfi_buf.dw_cfi_opc = DW_CFA_def_cfa_sf;
+		  cfi_buf.dw_cfi_oprnd2 = cfi_cfa_offset->dw_cfi_oprnd1;
+		  break;
+		case DW_CFA_def_cfa:
+		case DW_CFA_def_cfa_sf:
+		  cfi_buf.dw_cfi_opc = cfi_cfa_offset->dw_cfi_opc;
+		  cfi_buf.dw_cfi_oprnd2 = cfi_cfa_offset->dw_cfi_oprnd2;
+		  break;
+		default:
+		  gcc_unreachable ();
+		}
+	      cfi_cfa = &cfi_buf;
+	    }
+	  else if (cfi_cfa_offset)
+	    cfi_cfa = cfi_cfa_offset;
+	  if (cfi_cfa)
+	    {
+	      if (do_cfi_asm)
+		output_cfi_directive (cfi_cfa);
+	      else
+		output_cfi (cfi_cfa, fde, for_eh);
+	    }
+	  cfi_cfa = NULL;
+	  cfi_cfa_offset = NULL;
+	  if (cfi_args_size
+	      && cfi_args_size->dw_cfi_oprnd1.dw_cfi_offset)
+	    {
+	      if (do_cfi_asm)
+		output_cfi_directive (cfi_args_size);
+	      else
+		output_cfi (cfi_args_size, fde, for_eh);
+	    }
+	  cfi_args_size = NULL;
+	  if (cfi == NULL)
+	    {
+	      VEC_free (dw_cfi_ref, heap, regs);
+	      return;
+	    }
+	  else if (do_cfi_asm)
+	    output_cfi_directive (cfi);
+	  else
+	    output_cfi (cfi, fde, for_eh);
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
     }
 }
 
+/* Like output_cfis, but emit all CFIs in the vector.  */
+static void
+output_all_cfis (cfi_vec vec, bool do_cfi_asm,
+		 dw_fde_ref fde, bool for_eh)
+{
+  output_cfis (vec, VEC_length (dw_cfi_ref, vec), do_cfi_asm, fde, for_eh);
+}
+
 /* Output one FDE.  */
 
 static void
@@ -3666,6 +3681,7 @@ output_fde (dw_fde_ref fde, bool for_eh,
 	    char *section_start_label, int fde_encoding, char *augmentation,
 	    bool any_lsda_needed, int lsda_encoding)
 {
+  int ix;
   const char *begin, *end;
   static unsigned int j;
   char l1[20], l2[20];
@@ -3773,31 +3789,31 @@ output_fde (dw_fde_ref fde, bool for_eh,
      this FDE.  */
   fde->dw_fde_current_label = begin;
   if (!fde->dw_fde_switched_sections)
-    for (cfi = fde->dw_fde_cfi; cfi != NULL; cfi = cfi->dw_cfi_next)
+    FOR_EACH_VEC_ELT (dw_cfi_ref, fde->dw_fde_cfi, ix, cfi)
       output_cfi (cfi, fde, for_eh);
   else if (!second)
     {
-      if (fde->dw_fde_switch_cfi)
-	for (cfi = fde->dw_fde_cfi; cfi != NULL; cfi = cfi->dw_cfi_next)
+      if (fde->dw_fde_switch_cfi_index)
+	FOR_EACH_VEC_ELT (dw_cfi_ref, fde->dw_fde_cfi, ix, cfi)
 	  {
 	    output_cfi (cfi, fde, for_eh);
-	    if (cfi == fde->dw_fde_switch_cfi)
+	    if (ix == fde->dw_fde_switch_cfi_index)
 	      break;
 	  }
     }
   else
     {
-      dw_cfi_ref cfi_next = fde->dw_fde_cfi;
+      int i, from = 0;
+      int until = VEC_length (dw_cfi_ref, fde->dw_fde_cfi);
 
-      if (fde->dw_fde_switch_cfi)
+      if (fde->dw_fde_switch_cfi_index > 0)
 	{
-	  cfi_next = fde->dw_fde_switch_cfi->dw_cfi_next;
-	  fde->dw_fde_switch_cfi->dw_cfi_next = NULL;
-	  output_cfis (fde->dw_fde_cfi, false, fde, for_eh);
-	  fde->dw_fde_switch_cfi->dw_cfi_next = cfi_next;
+	  from = fde->dw_fde_switch_cfi_index;
+	  output_cfis (fde->dw_fde_cfi, from, false, fde, for_eh);
 	}
-      for (cfi = cfi_next; cfi != NULL; cfi = cfi->dw_cfi_next)
-	output_cfi (cfi, fde, for_eh);
+      for (i = from; i < until; i++)
+	output_cfi (VEC_index (dw_cfi_ref, fde->dw_fde_cfi, i),
+		    fde, for_eh);
     }
 
   /* If we are to emit a ref/link from function bodies to their frame tables,
@@ -4033,7 +4049,7 @@ output_call_frame_info (int for_eh)
 			     eh_data_format_name (fde_encoding));
     }
 
-  for (cfi = cie_cfi_head; cfi != NULL; cfi = cfi->dw_cfi_next)
+  FOR_EACH_VEC_ELT (dw_cfi_ref, cie_cfi_vec, i, cfi)
     output_cfi (cfi, NULL, for_eh);
 
   /* Pad the CIE out to an address sized boundary.  */
@@ -4179,8 +4195,8 @@ dwarf2out_begin_prologue (unsigned int l
   fde->dw_fde_end = NULL;
   fde->dw_fde_vms_end_prologue = NULL;
   fde->dw_fde_vms_begin_epilogue = NULL;
-  fde->dw_fde_cfi = NULL;
-  fde->dw_fde_switch_cfi = NULL;
+  fde->dw_fde_cfi = VEC_alloc (dw_cfi_ref, gc, 20);
+  fde->dw_fde_switch_cfi_index = 0;
   fde->funcdef_number = current_function_funcdef_no;
   fde->all_throwers_are_sibcalls = crtl->all_throwers_are_sibcalls;
   fde->uses_eh_lsda = crtl->uses_eh_lsda;
@@ -4385,18 +4401,10 @@ dwarf2out_switch_text_section (void)
       dwarf2out_do_cfi_startproc (true);
       /* As this is a different FDE, insert all current CFI instructions
 	 again.  */
-      output_cfis (fde->dw_fde_cfi, true, fde, true);
+      output_all_cfis (fde->dw_fde_cfi, true, fde, true);
     }
   else
-    {
-      dw_cfi_ref cfi = fde->dw_fde_cfi;
-
-      cfi = fde->dw_fde_cfi;
-      if (cfi)
-	while (cfi->dw_cfi_next != NULL)
-	  cfi = cfi->dw_cfi_next;
-      fde->dw_fde_switch_cfi = cfi;
-    }
+    fde->dw_fde_switch_cfi_index = VEC_length (dw_cfi_ref, fde->dw_fde_cfi);
 }
 
 /* And now, the subset of the debugging information support code necessary
@@ -17258,6 +17266,7 @@ tree_add_const_value_attribute_for_decl 
 static dw_loc_list_ref
 convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset)
 {
+  int ix;
   dw_fde_ref fde;
   dw_loc_list_ref list, *list_tail;
   dw_cfi_ref cfi;
@@ -17280,13 +17289,13 @@ convert_cfa_to_fb_loc_list (HOST_WIDE_IN
 
   /* ??? Bald assumption that the CIE opcode list does not contain
      advance opcodes.  */
-  for (cfi = cie_cfi_head; cfi; cfi = cfi->dw_cfi_next)
+  FOR_EACH_VEC_ELT (dw_cfi_ref, cie_cfi_vec, ix, cfi)
     lookup_cfa_1 (cfi, &next_cfa, &remember);
 
   last_cfa = next_cfa;
   last_label = start_label;
 
-  for (cfi = fde->dw_fde_cfi; cfi; cfi = cfi->dw_cfi_next)
+  FOR_EACH_VEC_ELT (dw_cfi_ref, fde->dw_fde_cfi, ix, cfi)
     switch (cfi->dw_cfi_opc)
       {
       case DW_CFA_set_loc:
Index: gcc/dwarf2out.c
===================================================================
--- gcc.orig/dwarf2out.c
+++ gcc/dwarf2out.c
@@ -471,13 +471,14 @@ static void initial_return_save (rtx);
 static HOST_WIDE_INT stack_adjust_offset (const_rtx, HOST_WIDE_INT,
 					  HOST_WIDE_INT);
 static void output_cfi (dw_cfi_ref, dw_fde_ref, int);
-static void output_cfi_directive (dw_cfi_ref);
+static void output_cfi_directive (FILE *, dw_cfi_ref);
 static void output_call_frame_info (int);
 static void dwarf2out_note_section_used (void);
 static bool clobbers_queued_reg_save (const_rtx);
 static void dwarf2out_frame_debug_expr (rtx, const char *);
-static void dwarf2out_cfi_begin_epilogue (rtx);
-static void dwarf2out_frame_debug_restore_state (void);
+
+extern void dwarf2out_cfi_begin_epilogue (rtx);
+extern void dwarf2out_frame_debug_restore_state (const char *);
 
 /* Support for complex CFA locations.  */
 static void output_cfa_loc (dw_cfi_ref, int);
@@ -889,6 +890,17 @@ static bool emit_cfa_remember;
 /* True if any CFI directives were emitted at the current insn.  */
 static bool any_cfis_emitted;
 
+static void
+add_cfa_remember (const char *label)
+{
+  dw_cfi_ref cfi_remember;
+
+  /* Emit the state save.  */
+  cfi_remember = new_cfi ();
+  cfi_remember->dw_cfi_opc = DW_CFA_remember_state;
+  add_fde_cfi (label, cfi_remember);
+}
+
 /* Add CFI to the current fde at the PC value indicated by LABEL if specified,
    or to the CIE if LABEL is NULL.  */
 
@@ -904,13 +916,8 @@ add_fde_cfi (const char *label, dw_cfi_r
 
   if (emit_cfa_remember)
     {
-      dw_cfi_ref cfi_remember;
-
-      /* Emit the state save.  */
       emit_cfa_remember = false;
-      cfi_remember = new_cfi ();
-      cfi_remember->dw_cfi_opc = DW_CFA_remember_state;
-      add_fde_cfi (label, cfi_remember);
+      add_cfa_remember (label);
     }
 
   if (dwarf2out_do_cfi_asm ())
@@ -1436,7 +1443,7 @@ stack_adjust_offset (const_rtx pattern, 
 
   return offset;
 }
-
+#if 0
 /* Precomputed args_size for CODE_LABELs and BARRIERs preceeding them,
    indexed by INSN_UID.  */
 
@@ -1541,7 +1548,11 @@ compute_barrier_args_size (void)
 	  cur_args_size = barrier_args_size[INSN_UID (insn)];
 	  prev = prev_nonnote_insn (insn);
 	  if (prev && BARRIER_P (prev))
-	    barrier_args_size[INSN_UID (prev)] = cur_args_size;
+	    {
+	      gcc_assert (LABEL_P (insn));
+	      barrier_args_size[INSN_UID (prev)] = cur_args_size;
+	      barrier_args_size[INSN_UID (insn)] = cur_args_size;
+	    }
 
 	  for (; insn; insn = NEXT_INSN (insn))
 	    {
@@ -1609,6 +1620,7 @@ compute_barrier_args_size (void)
   VEC_free (rtx, heap, worklist);
   VEC_free (rtx, heap, next);
 }
+#endif
 
 /* Add a CFI to update the running total of the size of arguments
    pushed onto the stack.  */
@@ -1707,25 +1719,7 @@ dwarf2out_notice_stack_adjust (rtx insn,
       return;
     }
   else if (BARRIER_P (insn))
-    {
-      /* Don't call compute_barrier_args_size () if the only
-	 BARRIER is at the end of function.  */
-      if (barrier_args_size == NULL && next_nonnote_insn (insn))
-	compute_barrier_args_size ();
-      if (barrier_args_size == NULL)
-	offset = 0;
-      else
-	{
-	  offset = barrier_args_size[INSN_UID (insn)];
-	  if (offset < 0)
-	    offset = 0;
-	}
-
-      offset -= args_size;
-#ifndef STACK_GROWS_DOWNWARD
-      offset = -offset;
-#endif
-    }
+    return;
   else if (GET_CODE (PATTERN (insn)) == SET)
     offset = stack_adjust_offset (PATTERN (insn), args_size, 0);
   else if (GET_CODE (PATTERN (insn)) == PARALLEL
@@ -2921,11 +2915,102 @@ dwarf2out_frame_debug (rtx insn, bool af
     dwarf2out_flush_queued_reg_saves ();
 }
 
+typedef struct
+{
+  cfi_vec cfis;
+  dw_cfa_location cfa, cfa_store;
+  bool visited;
+  bool used_as_start;
+  int args_size;
+} jump_target_info;
+
+static void
+maybe_record_jump_target (rtx label, VEC (rtx, heap) **worklist,
+			  int *uid_luid, jump_target_info *info)
+{
+  dw_fde_ref fde = current_fde ();
+  int uid;
+
+  if (GET_CODE (label) == LABEL_REF)
+    label = XEXP (label, 0);
+  gcc_assert (LABEL_P (label));
+  uid = INSN_UID (label);
+  info += uid_luid[uid];
+  if (info->visited || info->cfis)
+    return;
+
+  if (dump_file)
+    fprintf (dump_file, "recording label %d as possible jump target\n", uid);
+
+  VEC_safe_push (rtx, heap, *worklist, label);
+  info->cfis = VEC_copy (dw_cfi_ref, gc, fde->dw_fde_cfi);
+  info->args_size = args_size;
+  info->cfa = cfa;
+  info->cfa_store = cfa_store;
+}
+
+static bool
+vec_is_prefix_of (cfi_vec vec1, cfi_vec vec2)
+{
+  int i;
+  int len1 = VEC_length (dw_cfi_ref, vec1);
+  int len2 = VEC_length (dw_cfi_ref, vec2);
+  if (len1 > len2)
+    return false;
+  for (i = 0; i < len1; i++)
+    if (VEC_index (dw_cfi_ref, vec1, i) != VEC_index (dw_cfi_ref, vec1, i))
+      return false;
+  return true;
+}
+
+static void
+append_extra_cfis (dw_fde_ref fde, cfi_vec prefix, cfi_vec full, const char *label)
+{
+  int i;
+  int len = VEC_length (dw_cfi_ref, full);
+  int prefix_len = VEC_length (dw_cfi_ref, prefix);
+  for (i = 0; i < len; i++)
+    {
+      dw_cfi_ref elt = VEC_index (dw_cfi_ref, full, i);
+      if (i < prefix_len)
+	gcc_assert (elt == VEC_index (dw_cfi_ref, prefix, i));
+      else
+	{
+	  if (label)
+	    {
+	      dw_cfi_ref cfi2 = new_cfi ();
+	      *cfi2 = *elt;
+	      add_fde_cfi (label, cfi2);
+	    }
+	  else
+	    VEC_safe_push (dw_cfi_ref, gc, fde->dw_fde_cfi, elt);
+	}
+    }
+}
+
+extern void debug_cfi_vec (FILE *, cfi_vec v);
+void debug_cfi_vec (FILE *f, cfi_vec v)
+{
+  int ix;
+  dw_cfi_ref cfi;
+
+  FOR_EACH_VEC_ELT (dw_cfi_ref, v, ix, cfi)
+    output_cfi_directive (f, cfi);
+}
+
 void
 dwarf2out_frame_debug_init (void)
 {
-  size_t i;
+  int max_uid = get_max_uid ();
+  size_t j;
+  int i;
   rtx insn;
+  VEC (rtx, heap) *worklist;
+  cfi_vec incoming_cfis;
+  int n_points;
+  int *uid_luid;
+  jump_target_info *point_info;
+  dw_fde_ref fde = current_fde ();
 
   /* Flush any queued register saves.  */
   dwarf2out_flush_queued_reg_saves ();
@@ -2940,70 +3025,249 @@ dwarf2out_frame_debug_init (void)
   cfa_temp.reg = -1;
   cfa_temp.offset = 0;
 
-  for (i = 0; i < num_regs_saved_in_regs; i++)
+  for (j = 0; j < num_regs_saved_in_regs; j++)
     {
-      regs_saved_in_regs[i].orig_reg = NULL_RTX;
-      regs_saved_in_regs[i].saved_in_reg = NULL_RTX;
+      regs_saved_in_regs[j].orig_reg = NULL_RTX;
+      regs_saved_in_regs[j].saved_in_reg = NULL_RTX;
     }
   num_regs_saved_in_regs = 0;
-
+#if 0
   if (barrier_args_size)
     {
       XDELETEVEC (barrier_args_size);
       barrier_args_size = NULL;
     }
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+#endif
+
+  n_points = 0;
+  for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
+    if (LABEL_P (insn) || BARRIER_P (insn))
+      n_points++;
+  uid_luid = XCNEWVEC (int, max_uid);
+  n_points = 0;
+  for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
+    if (LABEL_P (insn) || BARRIER_P (insn))
+      uid_luid[INSN_UID (insn)] = n_points++;
+
+  point_info = XCNEWVEC (jump_target_info, n_points);
+  for (i = 0; i < n_points; i++)
+    point_info[i].args_size = -1;
+
+  worklist = VEC_alloc (rtx, heap, 20);
+  insn = get_insns ();
+  incoming_cfis = VEC_copy (dw_cfi_ref, gc, fde->dw_fde_cfi);
+
+  args_size = old_args_size = 0;
+
+  for (;;)
     {
-      rtx pat;
-      if (BARRIER_P (insn))
-	{
-	  dwarf2out_frame_debug (insn, false);
-	  continue;
-	}
-      else if (NOTE_P (insn))
-	{
-	  switch (NOTE_KIND (insn))
+      HOST_WIDE_INT offset;
+      rtx new_insn, best, next;
+      bool best_has_barrier;
+      jump_target_info *restart_info;
+
+      for (; insn != NULL_RTX; insn = next)
+	{
+	  int uid = INSN_UID (insn);
+	  rtx pat, note;
+
+	  next = NEXT_INSN (insn);
+	  if (LABEL_P (insn) || BARRIER_P (insn))
+	    {
+	      int luid = uid_luid[uid];
+	      jump_target_info *info = point_info + luid;
+	      if (info->used_as_start)
+		{
+		  if (dump_file)
+		    fprintf (dump_file,
+			     "Stopping scan at insn %d; previously reached\n",
+			     uid);
+		  break;
+		}
+	      info->visited = true;
+	      if (BARRIER_P (insn))
+		{
+		  dwarf2out_frame_debug (insn, false);
+		  gcc_assert (info->cfis == NULL);
+		  info->cfis = fde->dw_fde_cfi;
+		  if (dump_file)
+		    {
+		      fprintf (dump_file, "Stopping scan at barrier %d\n", uid);
+		      if (dump_flags & TDF_DETAILS)
+			debug_cfi_vec (dump_file, fde->dw_fde_cfi);
+		    }
+		  break;
+		}
+	    }
+	  if (!NONDEBUG_INSN_P (insn))
+	    continue;
+	  pat = PATTERN (insn);
+	  if (asm_noperands (pat) >= 0)
+	    continue;
+	  if (GET_CODE (pat) == SEQUENCE)
 	    {
-	    case NOTE_INSN_EPILOGUE_BEG:
-#if defined (HAVE_epilogue)
-	      dwarf2out_cfi_begin_epilogue (insn);
+	      for (i = 1; i < XVECLEN (pat, 0); i++)
+		dwarf2out_frame_debug (XVECEXP (pat, 0, i), false);
+	      insn = XVECEXP (pat, 0, 0);
+	    }
+
+	  if (CALL_P (insn) && dwarf2out_do_frame ())
+	    dwarf2out_frame_debug (insn, false);
+	  if (dwarf2out_do_frame ()
+#if !defined (HAVE_prologue)
+	      && !ACCUMULATE_OUTGOING_ARGS 
 #endif
-	      break;
-	    case NOTE_INSN_CFA_RESTORE_STATE:
-	      cfi_insn = insn;
-	      dwarf2out_frame_debug_restore_state ();
-	      break;
+	      )
+	    dwarf2out_frame_debug (insn, true);
+	  if (JUMP_P (insn))
+	    {
+	      rtx label = JUMP_LABEL (insn);
+	      if (label)
+		{
+		  rtx next = next_real_insn (label);
+		  if (next && JUMP_P (next)
+		      && (GET_CODE (PATTERN (next)) == ADDR_VEC
+			  || GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC))
+		    {
+		      rtx pat = PATTERN (next);
+		      int eltnum = GET_CODE (pat) == ADDR_DIFF_VEC ? 1 : 0;
+
+		      for (i = 0; i < XVECLEN (pat, eltnum); i++)
+			maybe_record_jump_target (XVECEXP (pat, eltnum, i),
+						  &worklist, uid_luid,
+						  point_info);
+		    }
+		  else
+		    maybe_record_jump_target (label, &worklist, uid_luid,
+					      point_info);
+		}
+	    }
+	  note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
+	  if (note)
+	    {
+	      eh_landing_pad lp;
+
+	      lp = get_eh_landing_pad_from_rtx (insn);
+	      if (lp)
+		maybe_record_jump_target (lp->landing_pad, &worklist,
+					  uid_luid, point_info);
 	    }
-	  continue;
 	}
-      if (!NONDEBUG_INSN_P (insn))
-	continue;
-      pat = PATTERN (insn);
-      if (asm_noperands (pat) >= 0)
-	continue;
-      if (GET_CODE (pat) == SEQUENCE)
+      best = NULL_RTX;
+      best_has_barrier = false;
+      FOR_EACH_VEC_ELT (rtx, worklist, i, new_insn)
 	{
-	  int j;
-	  for (j = 1; j < XVECLEN (pat, 0); j++)
-	    dwarf2out_frame_debug (XVECEXP (pat, 0, j), false);
-	  insn = XVECEXP (pat, 0, 0);
+	  rtx prev;
+	  bool this_has_barrier;
+	  restart_info = point_info + uid_luid[INSN_UID (new_insn)];
+	  if (restart_info->visited)
+	    continue;
+	  prev = prev_nonnote_nondebug_insn (new_insn);
+	  this_has_barrier = prev && BARRIER_P (prev);
+	  if (best == NULL_RTX
+	      || prev == insn
+	      || (!best_has_barrier && this_has_barrier))
+	    {
+	      best = new_insn;
+	      best_has_barrier = this_has_barrier;
+	    }
 	}
+      if (best == NULL_RTX)
+	break;
+      if (dump_file)
+	fprintf (dump_file, "restarting scan at label %d", INSN_UID (best));
+      restart_info = point_info + uid_luid[INSN_UID (best)];
+      restart_info->used_as_start = true;
+      insn = best;
+      gcc_assert (LABEL_P (insn));
+      fde->dw_fde_cfi = VEC_copy (dw_cfi_ref, gc, restart_info->cfis);
+      cfa = restart_info->cfa;
+      cfa_store = restart_info->cfa_store;
+      offset = restart_info->args_size;
+      if (offset >= 0)
+	{
+	  const char *label;
+
+	  if (dump_file && offset != args_size)
+	    fprintf (dump_file, ", args_size " HOST_WIDE_INT_PRINT_DEC
+		     "  -> " HOST_WIDE_INT_PRINT_DEC,
+		     args_size, offset);
 
-      if (CALL_P (insn) && dwarf2out_do_frame ())
-	dwarf2out_frame_debug (insn, false);
-      if (dwarf2out_do_frame ()
-#if !defined (HAVE_prologue)
-	  && !ACCUMULATE_OUTGOING_ARGS 
+	  offset -= args_size;
+#ifndef STACK_GROWS_DOWNWARD
+	  offset = -offset;
 #endif
-	  )
-	dwarf2out_frame_debug (insn, true);
+	  label = dwarf2out_cfi_label (false);
+	  cfi_insn = prev_nonnote_nondebug_insn (insn);
+	  dwarf2out_stack_adjust (offset, label);
+	}
+      if (dump_file)
+	{
+	  fprintf (dump_file, "\n");
+	  if (dump_flags & TDF_DETAILS)
+	    debug_cfi_vec (dump_file, fde->dw_fde_cfi);
+	}
+
+      restart_info->visited = true;
+      insn = NEXT_INSN (insn);
+    }
+
+  /* Now splice the various CFI fragments together into a coherent whole.  */
+  fde->dw_fde_cfi = VEC_alloc (dw_cfi_ref, gc, 20);
+  insn = get_insns ();
+  while (!NOTE_P (insn) && NOTE_KIND (insn) != NOTE_INSN_FUNCTION_BEG)
+    insn = NEXT_INSN (insn);
+  cfi_insn = insn;
+  add_cfa_remember (dwarf2out_cfi_label (false));
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      if (BARRIER_P (insn))
+	{
+	  cfi_vec new_cfi_vec;
+	  jump_target_info *info = point_info + uid_luid[INSN_UID (insn)];
+	  cfi_vec barrier_cfi = info->cfis;
+	  const char *label = dwarf2out_cfi_label (false);
+
+	  append_extra_cfis (fde, incoming_cfis, barrier_cfi, NULL);
+
+	  /* Find the start of the next sequence we processed.  */
+	  do
+	    {
+	      if (LABEL_P (insn))
+		{
+		  info = point_info + uid_luid[INSN_UID (insn)];
+		  if (info->used_as_start)
+		    break;
+		}
+	      insn = NEXT_INSN (insn);
+	    }
+	  while (insn != NULL_RTX);
+	  if (insn == NULL_RTX)
+	    break;
+
+	  /* Emit extra CFIs as necessary to achieve the correct state.  */
+	  gcc_assert (LABEL_P (insn));
+	  new_cfi_vec = info->cfis;
+	  cfi_insn = insn;
+	  if (vec_is_prefix_of (barrier_cfi, new_cfi_vec))
+	    append_extra_cfis (fde, barrier_cfi, new_cfi_vec, label);
+	  else
+	    {
+	      dwarf2out_frame_debug_restore_state (label);
+	      add_cfa_remember (label);
+
+	      append_extra_cfis (fde, NULL, new_cfi_vec, label);
+	    }
+	  incoming_cfis = new_cfi_vec;
+	}
     }
 }
 
 void
 dwarf2out_emit_cfi (dw_cfi_ref cfi)
 {
-  output_cfi_directive (cfi);
+  output_cfi_directive (asm_out_file, cfi);
 }
 
 /* Determine if we need to save and restore CFI information around
@@ -3091,17 +3355,12 @@ dwarf2out_cfi_begin_epilogue (rtx insn)
    required.  */
 
 void
-dwarf2out_frame_debug_restore_state (void)
+dwarf2out_frame_debug_restore_state (const char *label)
 {
   dw_cfi_ref cfi = new_cfi ();
-  const char *label = dwarf2out_cfi_label (false);
 
   cfi->dw_cfi_opc = DW_CFA_restore_state;
   add_fde_cfi (label, cfi);
-
-  gcc_assert (cfa_remember.in_use);
-  cfa = cfa_remember;
-  cfa_remember.in_use = 0;
 }
 
 /* Describe for the GTY machinery what parts of dw_cfi_oprnd1 are used.  */
@@ -3401,7 +3660,7 @@ output_cfi (dw_cfi_ref cfi, dw_fde_ref f
 /* Similar, but do it via assembler directives instead.  */
 
 static void
-output_cfi_directive (dw_cfi_ref cfi)
+output_cfi_directive (FILE *f, dw_cfi_ref cfi)
 {
   unsigned long r, r2;
 
@@ -3422,76 +3681,76 @@ output_cfi_directive (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 (asm_out_file, "\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;
 
     case DW_CFA_restore:
     case DW_CFA_restore_extended:
       r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1);
-      fprintf (asm_out_file, "\t.cfi_restore %lu\n", r);
+      fprintf (f, "\t.cfi_restore %lu\n", r);
       break;
 
     case DW_CFA_undefined:
       r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1);
-      fprintf (asm_out_file, "\t.cfi_undefined %lu\n", r);
+      fprintf (f, "\t.cfi_undefined %lu\n", r);
       break;
 
     case DW_CFA_same_value:
       r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1);
-      fprintf (asm_out_file, "\t.cfi_same_value %lu\n", r);
+      fprintf (f, "\t.cfi_same_value %lu\n", r);
       break;
 
     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 (asm_out_file, "\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;
 
     case DW_CFA_def_cfa_register:
       r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1);
-      fprintf (asm_out_file, "\t.cfi_def_cfa_register %lu\n", r);
+      fprintf (f, "\t.cfi_def_cfa_register %lu\n", r);
       break;
 
     case DW_CFA_register:
       r = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd1.dw_cfi_reg_num, 1);
       r2 = DWARF2_FRAME_REG_OUT (cfi->dw_cfi_oprnd2.dw_cfi_reg_num, 1);
-      fprintf (asm_out_file, "\t.cfi_register %lu, %lu\n", r, r2);
+      fprintf (f, "\t.cfi_register %lu, %lu\n", r, r2);
       break;
 
     case DW_CFA_def_cfa_offset:
     case DW_CFA_def_cfa_offset_sf:
-      fprintf (asm_out_file, "\t.cfi_def_cfa_offset "
+      fprintf (f, "\t.cfi_def_cfa_offset "
 	       HOST_WIDE_INT_PRINT_DEC"\n",
 	       cfi->dw_cfi_oprnd1.dw_cfi_offset);
       break;
 
     case DW_CFA_remember_state:
-      fprintf (asm_out_file, "\t.cfi_remember_state\n");
+      fprintf (f, "\t.cfi_remember_state\n");
       break;
     case DW_CFA_restore_state:
-      fprintf (asm_out_file, "\t.cfi_restore_state\n");
+      fprintf (f, "\t.cfi_restore_state\n");
       break;
 
     case DW_CFA_GNU_args_size:
-      fprintf (asm_out_file, "\t.cfi_escape %#x,", DW_CFA_GNU_args_size);
+      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 (asm_out_file, "\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', asm_out_file);
+      fputc ('\n', f);
       break;
 
     case DW_CFA_GNU_window_save:
-      fprintf (asm_out_file, "\t.cfi_window_save\n");
+      fprintf (f, "\t.cfi_window_save\n");
       break;
 
     case DW_CFA_def_cfa_expression:
     case DW_CFA_expression:
-      fprintf (asm_out_file, "\t.cfi_escape %#x,", cfi->dw_cfi_opc);
+      fprintf (f, "\t.cfi_escape %#x,", cfi->dw_cfi_opc);
       output_cfa_loc_raw (cfi);
-      fputc ('\n', asm_out_file);
+      fputc ('\n', f);
       break;
 
     default:
@@ -3601,7 +3860,7 @@ output_cfis (cfi_vec vec, int upto, bool
 		  && cfi2->dw_cfi_opc != DW_CFA_restore_extended)
 		{
 		  if (do_cfi_asm)
-		    output_cfi_directive (cfi2);
+		    output_cfi_directive (asm_out_file, cfi2);
 		  else
 		    output_cfi (cfi2, fde, for_eh);
 		}
@@ -3635,7 +3894,7 @@ output_cfis (cfi_vec vec, int upto, bool
 	  if (cfi_cfa)
 	    {
 	      if (do_cfi_asm)
-		output_cfi_directive (cfi_cfa);
+		output_cfi_directive (asm_out_file, cfi_cfa);
 	      else
 		output_cfi (cfi_cfa, fde, for_eh);
 	    }
@@ -3645,7 +3904,7 @@ output_cfis (cfi_vec vec, int upto, bool
 	      && cfi_args_size->dw_cfi_oprnd1.dw_cfi_offset)
 	    {
 	      if (do_cfi_asm)
-		output_cfi_directive (cfi_args_size);
+		output_cfi_directive (asm_out_file, cfi_args_size);
 	      else
 		output_cfi (cfi_args_size, fde, for_eh);
 	    }
@@ -3656,7 +3915,7 @@ output_cfis (cfi_vec vec, int upto, bool
 	      return;
 	    }
 	  else if (do_cfi_asm)
-	    output_cfi_directive (cfi);
+	    output_cfi_directive (asm_out_file, cfi);
 	  else
 	    output_cfi (cfi, fde, for_eh);
 	  break;

Comments

Richard Henderson March 31, 2011, 9:28 p.m. UTC | #1
On 03/31/2011 12:59 PM, Bernd Schmidt wrote:
>> So long as late late compilation passes continue to not move frame-related
>> insns across basic block boundaries, we should be fine.
> 
> I'm nervous about this as the reorg pass can do arbitrary
> transformations. On Blackfin for example, we can reorder basic blocks
> for the sake of loop optimizations; sched-ebb can create new blocks,
> etc. I think it would be best if we can somehow make it work during
> final, without a CFG.

I guess that's the best thing for now.  I'm sure we all agree that long
term all transformations should preserve the CFG all the way through final.
At which point this could be implemented as a pass on the function after
all transformations are complete.

> Rather than use a CFG, I've tried to do something similar to
> compute_barrier_args_size, using JUMP_LABELs etc.

A reasonable solution for now, I suppose.

> 
> Summary of the patches:
> 001 - just create a dwarf2out_frame_debug_init function.

Ok.

> 002 - Make it walk the function in a first pass and record CFIs to
>       be output later

Do I correctly understand that NOTE_INSN_CFI isn't actually being
used in this patch?

> 003 - Store dw_cfi_refs in VECs rather than linked lists. Looks
>       larger than it is due to reindentation

Like 001, this one looks like it's totally independent of and of
the other changes, and a good cleanup.  Please go ahead and test
and commit this one independently.

> 004 - Change the function walk introduced in 002 so that it records
>       and restores state when reaching jumps/barriers

I'm not too fond of vec_is_prefix_of.  The Problem is that you're
not applying any understanding of the actual data, just doing a
string comparison (effectively).

Imagine two code paths A and B that both save R2 and R3 into their
respective stack slots.  Imagine that -- for whatever reason -- the
stores have been scheduled differently such that on path A R2 is 
saved before R3, and the reverse on path B.

Your prefix test will conclude that paths A and B end with different
unwind info, even though they are in fact compatible.

Using some mechanism by which we can compare aggregate CFI information
on a per-register basis ought to also vastly improve the efficiency in
adjusting the cfi info between code points.  It should also enable
proper information in the -freorder-blocks-and-partition case.

> * i386.c uses dwarf2out_frame_debug directly in some cases and is
>   unconverted

Hum.  I wonder what the best way to attack this.  It's a local change,
adjusting and then restoring the unwind state between two insns that
should not be scheduled separately.

We could turn them into two unspec_volatiles, and lose scheduling 
across this pattern.  But ideally this is a value that ought to be
shrink-wrapped.  It's expensive to compute, and there are many
early-return situations in which we don't need it.

I suppose we could split this pattern manually in i386 reorg; 
forcing this to be split before final even at -O0.  At that point
all shrink-wrapping would be done and an unspecv replacement 
would be ok.

> * I haven't tested whether my attempt to use
>   get_eh_landing_pad_from_rtx in the absence of a CFG actually works

It will.  This information is stored in cfun->eh.  By design this
information must survive until final, so that we can emit the 
actual eh info into the appropriate tables.

> * Computed jumps and nonlocal gotos aren't handled. I think this
>   could be done by recording the state at NOTE_INSN_PROLOGUE_END
>   and using that for all labels we can't otherwise reach.

That should be reasonable.  You could assert that all of these 
labels are in forced_labels.  All computed branch targets should
be listed therein.


r~
diff mbox

Patch

Index: gcc/dwarf2out.c
===================================================================
--- gcc.orig/dwarf2out.c
+++ gcc/dwarf2out.c
@@ -2790,38 +2790,6 @@  dwarf2out_frame_debug (rtx insn, bool af
   rtx note, n;
   bool handled_one = false;
 
-  if (insn == NULL_RTX)
-    {
-      size_t i;
-
-      /* Flush any queued register saves.  */
-      dwarf2out_flush_queued_reg_saves ();
-
-      /* Set up state for generating call frame debug info.  */
-      lookup_cfa (&cfa);
-      gcc_assert (cfa.reg
-		  == (unsigned long)DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM));
-
-      cfa.reg = STACK_POINTER_REGNUM;
-      cfa_store = cfa;
-      cfa_temp.reg = -1;
-      cfa_temp.offset = 0;
-
-      for (i = 0; i < num_regs_saved_in_regs; i++)
-	{
-	  regs_saved_in_regs[i].orig_reg = NULL_RTX;
-	  regs_saved_in_regs[i].saved_in_reg = NULL_RTX;
-	}
-      num_regs_saved_in_regs = 0;
-
-      if (barrier_args_size)
-	{
-	  XDELETEVEC (barrier_args_size);
-	  barrier_args_size = NULL;
-	}
-      return;
-    }
-
   if (!NONJUMP_INSN_P (insn) || clobbers_queued_reg_save (insn))
     dwarf2out_flush_queued_reg_saves ();
 
@@ -2939,6 +2907,38 @@  dwarf2out_frame_debug (rtx insn, bool af
     dwarf2out_flush_queued_reg_saves ();
 }
 
+void
+dwarf2out_frame_debug_init (void)
+{
+  size_t i;
+
+  /* Flush any queued register saves.  */
+  dwarf2out_flush_queued_reg_saves ();
+
+  /* Set up state for generating call frame debug info.  */
+  lookup_cfa (&cfa);
+  gcc_assert (cfa.reg
+	      == (unsigned long)DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM));
+
+  cfa.reg = STACK_POINTER_REGNUM;
+  cfa_store = cfa;
+  cfa_temp.reg = -1;
+  cfa_temp.offset = 0;
+
+  for (i = 0; i < num_regs_saved_in_regs; i++)
+    {
+      regs_saved_in_regs[i].orig_reg = NULL_RTX;
+      regs_saved_in_regs[i].saved_in_reg = NULL_RTX;
+    }
+  num_regs_saved_in_regs = 0;
+
+  if (barrier_args_size)
+    {
+      XDELETEVEC (barrier_args_size);
+      barrier_args_size = NULL;
+    }
+}
+
 /* Determine if we need to save and restore CFI information around this
    epilogue.  If SIBCALL is true, then this is a sibcall epilogue.  If
    we do need to save/restore, then emit the save now, and insert a
Index: gcc/dwarf2out.h
===================================================================
--- gcc.orig/dwarf2out.h
+++ gcc/dwarf2out.h
@@ -20,6 +20,7 @@  along with GCC; see the file COPYING3.  
 
 extern void dwarf2out_decl (tree);
 extern void dwarf2out_frame_debug (rtx, bool);
+extern void dwarf2out_frame_debug_init (void);
 extern void dwarf2out_cfi_begin_epilogue (rtx);
 extern void dwarf2out_frame_debug_restore_state (void);
 extern void dwarf2out_flush_queued_reg_saves (void);
Index: gcc/final.c
===================================================================
--- gcc.orig/final.c
+++ gcc/final.c
@@ -1561,7 +1561,7 @@  final_start_function (rtx first ATTRIBUT
 
 #if defined (HAVE_prologue)
   if (dwarf2out_do_frame ())
-    dwarf2out_frame_debug (NULL_RTX, false);
+    dwarf2out_frame_debug_init ();
 #endif
 
   /* If debugging, assign block numbers to all of the blocks in this