Patchwork [3/6] Allow jumps in epilogues

login
register
mail settings
Submitter Bernd Schmidt
Date April 13, 2011, 12:38 p.m.
Message ID <4DA5994E.3020407@codesourcery.com>
Download mbox | patch
Permalink /patch/91055/
State New
Headers show

Comments

Bernd Schmidt - April 13, 2011, 12:38 p.m.
On 04/11/2011 07:10 PM, Richard Henderson wrote:
> Ok.

Thanks, committed. And here's an update of the other patch set,
renumbered to start at 5, with a changelog to cover all three patches
together (which is how they'll need to be committed eventually).

005-scanfirst: Mostly identical to the previous version of the scanfirst
patch. Now deletes the CFI notes to avoid compare-debug
failures.

006-cfilabel: A new patch, which reduces the amount of different code
paths we can take in add_fde_cfi, as this was becoming unmanageable. The
concept is to first emit just the CFI notes, in all cases. Later, after
we're done producing the CFI insns we need, another pass over the rtl
adds the necessary labels and set_loc/advance_loc CFIs. One consequence
of this is that def_cfa_1 can no longer use lookup_cfa, so it just
compares to an old_cfa variable instead. This also requires
target-specific changes as some ports use dwarf2out_cfi_label. An
(untested) example of the necessary changes is in config/arm.

One thing that has disappeared is
-  /* ??? Of course, this heuristic fails when we're annotating epilogues,
-     because of course we'll always want to redefine the CFA back to the
-     stack pointer on the way out.  Where should we move this check?  */
because I didn't know what it was for, and "if (0)"ed anyway.

007-dw2cfg: A much extended version of the previous patch. Now does much
better placement of remember/restore; in almost all cases the code is
identical to what we currently generate, modulo minor differences around
the PROLOGUE_END label. I've made it emit queued register saves before
PROLOGUE_END so that we can use the state there for forced labels.

This bootstraps and tests ok on i686-linux. However, there is work left
to be done. Can I take you up on your offer to work with me on this?
This still requires the i386 output_set_got which I think I can cope
with, but the ia64 backend does a number of things with unwinding that I
don't understand. Also, I'll be away the next two weeks - if you arrive
at a complete version during that time it would be great if you could
commit it.

One thing to note is that it seems surprisingly hard to make
-freorder-blocks-and-partition do anything interesting. There's one C++
testcase (partition2.C I think) which I used to debug this code, but
other than that I haven't really found anything that actually generates
two nonempty partitions.


Bernd
* target.def (dwarf_handle_frame_unspec): Remove label argument.
	* doc/tm.texi: Regenerate.
	* tree.h (dwarf2out_cfi_label, dwarf2out_def_cfa,
	dwarf2out_window_save, dwarf2out_reg_save, dwarf2out_return_save,
	dwarf2out_return_reg, dwarf2out_reg_save_reg): Don't declare.
	* final.c (final_start_function): Call
	dwarf2out_frame_debug_after_prologue.
	(final_scan_insn): Don't call dwarf2out_frame_debug for anything.
	Handle NOTE_INSN_CFI and NOTE_INSN_CFI_LABEL.
	(final): Delete these notes.
	* insn-notes.def (CFI, CFI_LABEL): New.
	* jump.c (addr_vec_p): New function.
	* dwarf2out.c (cfi_insn): New static variable.
	(dwarf2out_cfi_label): Remove force argument. All callers changed.
	Only generate the label, don't emit it.
	(dwarf2out_maybe_emit_cfi_label): New function.
	
	(add_fde_cfi): Remove label argument.  All callers changed.  Remove
	most code; leave a condition to either emit a CFI insn, or add the
	CFI to the FDE CFI vector.
	(add_cie_cfi): New static function.
	(add_cfi): Remove function.
	(old_cfa): New static variable.
	(cfa_remember): Remove static variable.
	(dwarf2out_def_cfa): Replace label argument with a bool for_cie
	argument.  All callers changed.  Don't use lookup_cfa; use and
	update the global old_cfa variable.  Call add_fde_cfi or add_cie_cfi
	at the end.
	(reg_save): Replace label argument with a bool.  All callers changed.
	Call add_fde_cfi or add_cie_cfi at the end.
	(dwarf2out_reg_save, dwarf2out_return_save, dwarf2out_return_reg,
	dwarf2out_args_szie, dwarf2out_stack_adjust, dwarf2out_reg_save_reg,
	dwarf2out_frame_debug_def_cfa, dwarf2out_frame_debug_cfa_offset,
	dwarf2out_frame_debug_cfa_register, dwarf2out_frame_debug_cfa_restore,
	dwarf2out_frame_debug_cfa_expression, dwarf2out_frame_debug_expr):
	Remove label argument.  All callers changed.
	(barrier_args_size): Remove variable.
	(compute_barrier_args_size_1, compute_barrier_args_size): Remove
	functions.
	(dwarf2out_notice_stack_adjust): Don't handle barriers.
	(last_reg_save_label): Remove variable.  All sets and uses removed.
	(cfi_label_required_p, add_cfis_to_fde): New static functions.
	(dwarf2out_frame_debug_restore_state): Simply add the new CFI.
	(dwarf2out_frame_debug): Set cfi_insn, and clear it.  Don't call
	dwarf2out_flush_queued_reg_saves at the top.
	(dwarf2out_frame_debug_init): Initialize old_cfa.
	(copy_cfi_vec_parts): New static function.
	(jump_target_info): New struct type.
	(dwarf2out_cfi_begin_epilogue): Remove.
	(save_point_p, record_current_state, maybe_record_jump_target,
	vec_is_prefix_of, append_extra_cfis, debug_cfi_vec, switch_note_p,
	scan_until_barrier, find_best_starting_point): New static functions.
	(dwarf2out_frame_debug_after_prologue): New function.
	(dwarf2out_emit_cfi): New function.
	(output_cfi_directive): New FILE argument.  All callers changed.
	Avoid some paths if it is not asm_out_file; otherwise print to it.
	(output_all_cfis): Remove function.
	(output_cfis): Remove do_cfi_asm arg.  All callers changed.  Never
	call output_cfi_directive.
	(dwarf2out_frame_init): Initialize old_cfa.
	(dwarf2out_switch_text_section): Don't initialize dw_fde_current_label.
	Don't call output_all_cfis.
	* dwarf2out.h (dwarf2out_cfi_label, dwarf2out_def_cfa,
	dwarf2out_window_save, dwarf2out_reg_save, dwarf2out_return_save,
	dwarf2out_return_reg, dwarf2out_reg_save_reg, dwarf2out_emit_cfi,
	dwarf2out_frame_debug_after_prologue): Declare.
	(dwarf2out_cfi_begin_epilogue, dwarf2out_frame_debug_restore_state):
	Don't declare.
	(struct dw_cfi_struct): Add forward declaration.
	* rtl.h (union rtunion_def): Add rt_cfi member.
	(XCFI, XCCFI, NOTE_CFI, NOTE_LABEL_NUMBER): New macros.
	(addr_vec_p): Declare.
	* config/sparc/sparc.c (sparc_dwarf_handle_frame_unspec): Remove
	label argument.
	* config/ia64/ia64.c (ia64_dwarf_handle_frame_unspec): Likewise.
	* config/arm/arm.c (thumb_pushpop): Use dwarf2out_maybe_emit_cfi_label
	rather than dwarf2out_cfi_label.
	(thumb1_output_function_prologue): Likewise.
	(arm_dwarf_handle_frame_unspec): Remove label argument.
* 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.
---
 config/arm/arm.c     |    5 
 config/ia64/ia64.c   |    6 
 config/sparc/sparc.c |    7 
 config/vax/vax.c     |    2 
 dwarf2out.c          |  467 ++++++++++++++++++++++++---------------------------
 dwarf2out.h          |   32 +++
 final.c              |    5 
 target.def           |    2 
 tree.h               |   31 ---
 9 files changed, 270 insertions(+), 287 deletions(-)

Index: gcc/config/arm/arm.c
===================================================================
--- gcc.orig/config/arm/arm.c
+++ gcc/config/arm/arm.c
@@ -19977,18 +19977,19 @@ thumb_pushpop (FILE *f, unsigned long ma
 
   if (push && pushed_words && dwarf2out_do_frame ())
     {
-      char *l = dwarf2out_cfi_label (false);
       int pushed_mask = real_regs;
 
+      dwarf2out_maybe_emit_cfi_label ();
+
       *cfa_offset += pushed_words * 4;
-      dwarf2out_def_cfa (l, SP_REGNUM, *cfa_offset);
+      dwarf2out_def_cfa (SP_REGNUM, *cfa_offset);
 
       pushed_words = 0;
       pushed_mask = real_regs;
       for (regno = 0; regno <= 14; regno++, pushed_mask >>= 1)
 	{
 	  if (pushed_mask & 1)
-	    dwarf2out_reg_save (l, regno, 4 * pushed_words++ - *cfa_offset);
+	    dwarf2out_reg_save (regno, 4 * pushed_words++ - *cfa_offset);
 	}
     }
 }
@@ -20997,10 +20998,9 @@ thumb1_output_function_prologue (FILE *f
 	 the stack pointer.  */
       if (dwarf2out_do_frame ())
 	{
-	  char *l = dwarf2out_cfi_label (false);
-
+	  dwarf2out_maybe_emit_cfi_label ();
 	  cfa_offset = cfa_offset + crtl->args.pretend_args_size;
-	  dwarf2out_def_cfa (l, SP_REGNUM, cfa_offset);
+	  dwarf2out_def_cfa (SP_REGNUM, cfa_offset);
 	}
     }
 
@@ -21046,10 +21046,10 @@ thumb1_output_function_prologue (FILE *f
 
       if (dwarf2out_do_frame ())
 	{
-	  char *l = dwarf2out_cfi_label (false);
+	  dwarf2out_maybe_emit_cfi_label ();
 
 	  cfa_offset = cfa_offset + 16;
-	  dwarf2out_def_cfa (l, SP_REGNUM, cfa_offset);
+	  dwarf2out_def_cfa (SP_REGNUM, cfa_offset);
 	}
 
       if (l_mask)
@@ -22749,7 +22749,7 @@ arm_except_unwind_info (struct gcc_optio
    stack alignment.  */
 
 static void
-arm_dwarf_handle_frame_unspec (const char *label, rtx pattern, int index)
+arm_dwarf_handle_frame_unspec (rtx pattern, int index)
 {
   rtx unspec = SET_SRC (pattern);
   gcc_assert (GET_CODE (unspec) == UNSPEC);
@@ -22760,8 +22760,7 @@ arm_dwarf_handle_frame_unspec (const cha
       /* ??? We should set the CFA = (SP & ~7).  At this point we haven't
          put anything on the stack, so hopefully it won't matter.
          CFA = SP will be correct after alignment.  */
-      dwarf2out_reg_save_reg (label, stack_pointer_rtx,
-                              SET_DEST (pattern));
+      dwarf2out_reg_save_reg (stack_pointer_rtx, SET_DEST (pattern));
       break;
     default:
       gcc_unreachable ();
Index: gcc/config/ia64/ia64.c
===================================================================
--- gcc.orig/config/ia64/ia64.c
+++ gcc/config/ia64/ia64.c
@@ -330,7 +330,7 @@ static enum machine_mode ia64_promote_fu
 static void ia64_trampoline_init (rtx, tree, rtx);
 static void ia64_override_options_after_change (void);
 
-static void ia64_dwarf_handle_frame_unspec (const char *, rtx, int);
+static void ia64_dwarf_handle_frame_unspec (rtx, int);
 static tree ia64_builtin_decl (unsigned, bool);
 
 static reg_class_t ia64_preferred_reload_class (rtx, reg_class_t);
@@ -9710,9 +9710,7 @@ ia64_dwarf2out_def_steady_cfa (rtx insn,
    processing.  The real CFA definition is set up above.  */
 
 static void
-ia64_dwarf_handle_frame_unspec (const char * ARG_UNUSED (label),
-				rtx ARG_UNUSED (pattern),
-				int index)
+ia64_dwarf_handle_frame_unspec (rtx ARG_UNUSED (pattern), int index)
 {
   gcc_assert (index == UNSPECV_ALLOC);
 }
Index: gcc/config/sparc/sparc.c
===================================================================
--- gcc.orig/config/sparc/sparc.c
+++ gcc/config/sparc/sparc.c
@@ -454,7 +454,7 @@ static unsigned int sparc_function_arg_b
 						 const_tree);
 static int sparc_arg_partial_bytes (CUMULATIVE_ARGS *,
 				    enum machine_mode, tree, bool);
-static void sparc_dwarf_handle_frame_unspec (const char *, rtx, int);
+static void sparc_dwarf_handle_frame_unspec (rtx, int);
 static void sparc_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
 static void sparc_file_end (void);
 static bool sparc_frame_pointer_required (void);
@@ -9423,12 +9423,11 @@ get_some_local_dynamic_name_1 (rtx *px, 
    This is called from dwarf2out.c to emit call frame instructions
    for frame-related insns containing UNSPECs and UNSPEC_VOLATILEs. */
 static void
-sparc_dwarf_handle_frame_unspec (const char *label,
-				 rtx pattern ATTRIBUTE_UNUSED,
+sparc_dwarf_handle_frame_unspec (rtx pattern ATTRIBUTE_UNUSED,
 				 int index ATTRIBUTE_UNUSED)
 {
   gcc_assert (index == UNSPECV_SAVEW);
-  dwarf2out_window_save (label);
+  dwarf2out_window_save ();
 }
 
 /* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL.
Index: gcc/config/vax/vax.c
===================================================================
--- gcc.orig/config/vax/vax.c
+++ gcc/config/vax/vax.c
@@ -163,17 +163,18 @@ vax_output_function_prologue (FILE * fil
 
   if (dwarf2out_do_frame ())
     {
-      const char *label = dwarf2out_cfi_label (false);
       int offset = 0;
 
+      dwarf2out_maybe_emit_cfi_label ();
+
       for (regno = FIRST_PSEUDO_REGISTER-1; regno >= 0; --regno)
 	if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
-	  dwarf2out_reg_save (label, regno, offset -= 4);
+	  dwarf2out_reg_save (regno, offset -= 4);
 
-      dwarf2out_reg_save (label, PC_REGNUM, offset -= 4);
-      dwarf2out_reg_save (label, FRAME_POINTER_REGNUM, offset -= 4);
-      dwarf2out_reg_save (label, ARG_POINTER_REGNUM, offset -= 4);
-      dwarf2out_def_cfa (label, FRAME_POINTER_REGNUM, -(offset - 4));
+      dwarf2out_reg_save (PC_REGNUM, offset -= 4);
+      dwarf2out_reg_save (FRAME_POINTER_REGNUM, offset -= 4);
+      dwarf2out_reg_save (ARG_POINTER_REGNUM, offset -= 4);
+      dwarf2out_def_cfa (false, FRAME_POINTER_REGNUM, -(offset - 4));
     }
 
   size -= STARTING_FRAME_OFFSET;
Index: gcc/dwarf2out.c
===================================================================
--- gcc.orig/dwarf2out.c
+++ gcc/dwarf2out.c
@@ -456,11 +456,11 @@ 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 (cfi_vec *, dw_cfi_ref);
-static void add_fde_cfi (const char *, dw_cfi_ref);
+static void add_fde_cfi (dw_cfi_ref);
+static void add_cie_cfi (dw_cfi_ref);
 static void lookup_cfa_1 (dw_cfi_ref, dw_cfa_location *, dw_cfa_location *);
 static void lookup_cfa (dw_cfa_location *);
-static void reg_save (const char *, unsigned, unsigned, HOST_WIDE_INT);
+static void reg_save (bool, unsigned, unsigned, HOST_WIDE_INT);
 static void initial_return_save (rtx);
 static HOST_WIDE_INT stack_adjust_offset (const_rtx, HOST_WIDE_INT,
 					  HOST_WIDE_INT);
@@ -469,7 +469,7 @@ static void output_cfi_directive (dw_cfi
 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_frame_debug_expr (rtx);
 static void dwarf2out_cfi_begin_epilogue (rtx);
 static void dwarf2out_frame_debug_restore_state (void);
 
@@ -482,7 +482,7 @@ static struct dw_loc_descr_struct *build
   (dw_cfa_location *, HOST_WIDE_INT);
 static struct dw_loc_descr_struct *build_cfa_aligned_loc
   (HOST_WIDE_INT, HOST_WIDE_INT);
-static void def_cfa_1 (const char *, dw_cfa_location *);
+static void def_cfa_1 (bool, dw_cfa_location *);
 static struct dw_loc_descr_struct *mem_loc_descriptor
   (rtx, enum machine_mode mode, enum var_init_status);
 
@@ -820,35 +820,6 @@ new_cfi (void)
   return cfi;
 }
 
-/* Add a Call Frame Instruction to list of instructions.  */
-
-static inline void
-add_cfi (cfi_vec *vec, dw_cfi_ref cfi)
-{
-  dw_fde_ref fde = current_fde ();
-
-  /* When DRAP is used, CFA is defined with an expression.  Redefine
-     CFA may lead to a different CFA value.   */
-  /* ??? Of course, this heuristic fails when we're annotating epilogues,
-     because of course we'll always want to redefine the CFA back to the
-     stack pointer on the way out.  Where should we move this check?  */
-  if (0 && fde && fde->drap_reg != INVALID_REGNUM)
-    switch (cfi->dw_cfi_opc)
-      {
-        case DW_CFA_def_cfa_register:
-        case DW_CFA_def_cfa_offset:
-        case DW_CFA_def_cfa_offset_sf:
-        case DW_CFA_def_cfa:
-        case DW_CFA_def_cfa_sf:
-	  gcc_unreachable ();
-
-        default:
-          break;
-      }
-
-  VEC_safe_push (dw_cfi_ref, gc, *vec, cfi);
-}
-
 /* The insn after which a new CFI note should be emitted.  */
 static rtx cfi_insn;
 
@@ -858,45 +829,51 @@ static bool emit_cfa_remember;
 /* True if any CFI directives were emitted at the current insn.  */
 static bool any_cfis_emitted;
 
-/* Generate a new label for the CFI info to refer to.  FORCE is true
-   if a label needs to be output even when using .cfi_* directives.  */
+/* Generate a new label for the CFI info to refer to.  */
 
-char *
-dwarf2out_cfi_label (bool force)
+static char *
+dwarf2out_cfi_label (void)
 {
   static char label[20];
 
-  if (!force && dwarf2out_do_cfi_asm ())
-    {
-      /* In this case, we will be emitting the asm directive instead of
-	 the label, so just return a placeholder to keep the rest of the
-	 interfaces happy.  */
-      strcpy (label, "<do not output>");
-    }
-  else
-    {
-      int num = dwarf2out_cfi_label_num++;
-      ASM_GENERATE_INTERNAL_LABEL (label, "LCFI", num);
-      cfi_insn = emit_note_after (NOTE_INSN_CFI_LABEL, cfi_insn);
-      NOTE_LABEL_NUMBER (cfi_insn) = num;
-    }
+  int num = dwarf2out_cfi_label_num++;
+  ASM_GENERATE_INTERNAL_LABEL (label, "LCFI", num);
 
   return label;
 }
 
+/* Called by target specific code if it wants to emit CFI insns in the text
+   prologue.  If necessary, emit a CFI label and an advance_loc CFI.  See
+   also cfi_label_required_p.  */
+void
+dwarf2out_maybe_emit_cfi_label (void)
+{
+  if ((dwarf_version == 2
+       && debug_info_level > DINFO_LEVEL_TERSE
+       && (write_symbols == DWARF2_DEBUG
+	   || write_symbols == VMS_AND_DWARF2_DEBUG))
+      || !dwarf2out_do_cfi_asm ())
+    {
+      const char *l;
+      dw_cfi_ref xcfi;
+
+      ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LCFI", dwarf2out_cfi_label_num);
+      l = dwarf2out_cfi_label ();
+      l = xstrdup (l);
+
+      xcfi = new_cfi ();
+      xcfi->dw_cfi_opc = DW_CFA_advance_loc4;
+      xcfi->dw_cfi_oprnd1.dw_cfi_addr = l;
+      add_fde_cfi (xcfi);
+    }
+}
+
 /* Add CFI to the current fde at the PC value indicated by LABEL if specified,
    or to the CIE if LABEL is NULL.  */
 
 static void
-add_fde_cfi (const char *label, dw_cfi_ref cfi)
+add_fde_cfi (dw_cfi_ref cfi)
 {
-  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)
     {
       dw_cfi_ref cfi_remember;
@@ -905,110 +882,30 @@ add_fde_cfi (const char *label, dw_cfi_r
       emit_cfa_remember = false;
       cfi_remember = new_cfi ();
       cfi_remember->dw_cfi_opc = DW_CFA_remember_state;
-      add_fde_cfi (label, cfi_remember);
+      add_fde_cfi (cfi_remember);
     }
 
-  if (dwarf2out_do_cfi_asm ())
+  any_cfis_emitted = true;
+  if (cfi_insn != NULL)
     {
-      if (label)
-	{
-	  dw_fde_ref fde = current_fde ();
-
-	  gcc_assert (fde != NULL);
-
-	  /* We still have to add the cfi to the list so that lookup_cfa
-	     works later on.  When -g2 and above we even need to force
-	     emitting of CFI labels and add to list a DW_CFA_set_loc for
-	     convert_cfa_to_fb_loc_list purposes.  If we're generating
-	     DWARF3 output we use DW_OP_call_frame_cfa and so don't use
-	     convert_cfa_to_fb_loc_list.  */
-	  if (dwarf_version == 2
-	      && debug_info_level > DINFO_LEVEL_TERSE
-	      && (write_symbols == DWARF2_DEBUG
-		  || write_symbols == VMS_AND_DWARF2_DEBUG))
-	    {
-	      switch (cfi->dw_cfi_opc)
-		{
-		case DW_CFA_def_cfa_offset:
-		case DW_CFA_def_cfa_offset_sf:
-		case DW_CFA_def_cfa_register:
-		case DW_CFA_def_cfa:
-		case DW_CFA_def_cfa_sf:
-		case DW_CFA_def_cfa_expression:
-		case DW_CFA_restore_state:
-		  if (*label == 0 || strcmp (label, "<do not output>") == 0)
-		    label = dwarf2out_cfi_label (true);
-
-		  if (fde->dw_fde_current_label == NULL
-		      || strcmp (label, fde->dw_fde_current_label) != 0)
-		    {
-		      dw_cfi_ref xcfi;
-
-		      label = xstrdup (label);
-
-		      /* Set the location counter to the new label.  */
-		      xcfi = new_cfi ();
-		      /* It doesn't metter whether DW_CFA_set_loc
-		         or DW_CFA_advance_loc4 is added here, those aren't
-		         emitted into assembly, only looked up by
-		         convert_cfa_to_fb_loc_list.  */
-		      xcfi->dw_cfi_opc = DW_CFA_set_loc;
-		      xcfi->dw_cfi_oprnd1.dw_cfi_addr = label;
-		      add_cfi (&fde->dw_fde_cfi, xcfi);
-		      fde->dw_fde_current_label = label;
-		    }
-		  break;
-		default:
-		  break;
-	        }
-	    }
-
-	  cfi_insn = emit_note_after (NOTE_INSN_CFI, cfi_insn);
-	  NOTE_CFI (cfi_insn) = cfi;
-
-	  vec = &fde->dw_fde_cfi;
-	  any_cfis_emitted = true;
-	}
-      /* ??? If this is a CFI for the CIE, we don't emit.  This
-	 assumes that the standard CIE contents that the assembler
-	 uses matches the standard CIE contents that the compiler
-	 uses.  This is probably a bad assumption.  I'm not quite
-	 sure how to address this for now.  */
+      cfi_insn = emit_note_after (NOTE_INSN_CFI, cfi_insn);
+      NOTE_CFI (cfi_insn) = cfi;
     }
-  else if (label)
+  else
     {
       dw_fde_ref fde = current_fde ();
-
-      gcc_assert (fde != NULL);
-
-      if (*label == 0)
-	label = dwarf2out_cfi_label (false);
-
-      if (fde->dw_fde_current_label == NULL
-	  || strcmp (label, fde->dw_fde_current_label) != 0)
-	{
-	  dw_cfi_ref xcfi;
-
-	  label = xstrdup (label);
-
-	  /* Set the location counter to the new label.  */
-	  xcfi = new_cfi ();
-	  /* If we have a current label, advance from there, otherwise
-	     set the location directly using set_loc.  */
-	  xcfi->dw_cfi_opc = fde->dw_fde_current_label
-			     ? DW_CFA_advance_loc4
-			     : DW_CFA_set_loc;
-	  xcfi->dw_cfi_oprnd1.dw_cfi_addr = label;
-	  add_cfi (&fde->dw_fde_cfi, xcfi);
-
-	  fde->dw_fde_current_label = label;
-	}
-
-      vec = &fde->dw_fde_cfi;
-      any_cfis_emitted = true;
+      VEC_safe_push (dw_cfi_ref, gc, fde->dw_fde_cfi, cfi);
+      dwarf2out_emit_cfi (cfi);
     }
+}
+
+static void
+add_cie_cfi (dw_cfi_ref cfi)
+{
+  if (cie_cfi_vec == NULL)
+    cie_cfi_vec = VEC_alloc (dw_cfi_ref, gc, 20);
 
-  add_cfi (vec, cfi);
+  VEC_safe_push (dw_cfi_ref, gc, cie_cfi_vec, cfi);
 }
 
 /* Subroutine of lookup_cfa.  */
@@ -1076,6 +973,9 @@ lookup_cfa (dw_cfa_location *loc)
 /* The current rule for calculating the DWARF2 canonical frame address.  */
 static dw_cfa_location cfa;
 
+/* A copy of CFA, for comparison purposes  */
+static dw_cfa_location old_cfa;
+
 /* The register used for saving registers to the stack, and its offset
    from the CFA.  */
 static dw_cfa_location cfa_store;
@@ -1083,25 +983,27 @@ static dw_cfa_location cfa_store;
 /* The current save location around an epilogue.  */
 static dw_cfa_location cfa_remember;
 
+/* Like cfa_remember, but a copy of old_cfa.  */
+static dw_cfa_location old_cfa_remember;
+
 /* The running total of the size of arguments pushed onto the stack.  */
 static HOST_WIDE_INT args_size;
 
 /* The last args_size we actually output.  */
 static HOST_WIDE_INT old_args_size;
 
-/* Entry point to update the canonical frame address (CFA).
-   LABEL is passed to add_fde_cfi.  The value of CFA is now to be
-   calculated from REG+OFFSET.  */
+/* Entry point to update the canonical frame address (CFA).  The value
+   of CFA is now to be calculated from REG+OFFSET.  */
 
 void
-dwarf2out_def_cfa (const char *label, unsigned int reg, HOST_WIDE_INT offset)
+dwarf2out_def_cfa (bool for_cie, unsigned int reg, HOST_WIDE_INT offset)
 {
   dw_cfa_location loc;
   loc.indirect = 0;
   loc.base_offset = 0;
   loc.reg = reg;
   loc.offset = offset;
-  def_cfa_1 (label, &loc);
+  def_cfa_1 (for_cie, &loc);
 }
 
 /* Determine if two dw_cfa_location structures define the same data.  */
@@ -1120,10 +1022,10 @@ cfa_equal_p (const dw_cfa_location *loc1
    the dw_cfa_location structure.  */
 
 static void
-def_cfa_1 (const char *label, dw_cfa_location *loc_p)
+def_cfa_1 (bool for_cie, dw_cfa_location *loc_p)
 {
   dw_cfi_ref cfi;
-  dw_cfa_location old_cfa, loc;
+  dw_cfa_location loc;
 
   cfa = *loc_p;
   loc = *loc_p;
@@ -1132,7 +1034,6 @@ def_cfa_1 (const char *label, dw_cfa_loc
     cfa_store.offset = loc.offset;
 
   loc.reg = DWARF_FRAME_REGNUM (loc.reg);
-  lookup_cfa (&old_cfa);
 
   /* If nothing changed, no need to issue any call frame instructions.  */
   if (cfa_equal_p (&loc, &old_cfa))
@@ -1193,16 +1094,19 @@ def_cfa_1 (const char *label, dw_cfa_loc
       cfi->dw_cfi_oprnd1.dw_cfi_loc = loc_list;
     }
 
-  add_fde_cfi (label, cfi);
+  if (for_cie)
+    add_cie_cfi (cfi);
+  else
+    add_fde_cfi (cfi);
+  old_cfa = loc;
 }
 
 /* Add the CFI for saving a register.  REG is the CFA column number.
-   LABEL is passed to add_fde_cfi.
    If SREG is -1, the register is saved at OFFSET from the CFA;
    otherwise it is saved in SREG.  */
 
 static void
-reg_save (const char *label, unsigned int reg, unsigned int sreg, HOST_WIDE_INT offset)
+reg_save (bool for_cie, unsigned int reg, unsigned int sreg, HOST_WIDE_INT offset)
 {
   dw_cfi_ref cfi = new_cfi ();
   dw_fde_ref fde = current_fde ();
@@ -1238,10 +1142,13 @@ reg_save (const char *label, unsigned in
       cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg;
     }
 
-  add_fde_cfi (label, cfi);
+  if (for_cie)
+    add_cie_cfi (cfi);
+  else
+    add_fde_cfi (cfi);
 }
 
-/* Add the CFI for saving a register window.  LABEL is passed to reg_save.
+/* Add the CFI for saving a register window.
    This CFI tells the unwinder that it needs to restore the window registers
    from the previous frame's window save area.
 
@@ -1249,39 +1156,39 @@ reg_save (const char *label, unsigned in
    assuming 0(cfa)) and what registers are in the window.  */
 
 void
-dwarf2out_window_save (const char *label)
+dwarf2out_window_save (void)
 {
   dw_cfi_ref cfi = new_cfi ();
 
   cfi->dw_cfi_opc = DW_CFA_GNU_window_save;
-  add_fde_cfi (label, cfi);
+  add_fde_cfi (cfi);
 }
 
 /* Entry point for saving a register to the stack.  REG is the GCC register
    number.  LABEL and OFFSET are passed to reg_save.  */
 
 void
-dwarf2out_reg_save (const char *label, unsigned int reg, HOST_WIDE_INT offset)
+dwarf2out_reg_save (unsigned int reg, HOST_WIDE_INT offset)
 {
-  reg_save (label, DWARF_FRAME_REGNUM (reg), INVALID_REGNUM, offset);
+  reg_save (false, DWARF_FRAME_REGNUM (reg), INVALID_REGNUM, offset);
 }
 
 /* Entry point for saving the return address in the stack.
    LABEL and OFFSET are passed to reg_save.  */
 
 void
-dwarf2out_return_save (const char *label, HOST_WIDE_INT offset)
+dwarf2out_return_save (HOST_WIDE_INT offset)
 {
-  reg_save (label, DWARF_FRAME_RETURN_COLUMN, INVALID_REGNUM, offset);
+  reg_save (false, DWARF_FRAME_RETURN_COLUMN, INVALID_REGNUM, offset);
 }
 
 /* Entry point for saving the return address in a register.
    LABEL and SREG are passed to reg_save.  */
 
 void
-dwarf2out_return_reg (const char *label, unsigned int sreg)
+dwarf2out_return_reg (unsigned int sreg)
 {
-  reg_save (label, DWARF_FRAME_RETURN_COLUMN, DWARF_FRAME_REGNUM (sreg), 0);
+  reg_save (false, DWARF_FRAME_RETURN_COLUMN, DWARF_FRAME_REGNUM (sreg), 0);
 }
 
 /* Record the initial position of the return address.  RTL is
@@ -1339,7 +1246,7 @@ initial_return_save (rtx rtl)
     }
 
   if (reg != DWARF_FRAME_RETURN_COLUMN)
-    reg_save (NULL, DWARF_FRAME_RETURN_COLUMN, reg, offset - cfa.offset);
+    reg_save (true, DWARF_FRAME_RETURN_COLUMN, reg, offset - cfa.offset);
 }
 
 /* Given a SET, calculate the amount of stack adjustment it
@@ -1609,7 +1516,7 @@ compute_barrier_args_size (void)
    pushed onto the stack.  */
 
 static void
-dwarf2out_args_size (const char *label, HOST_WIDE_INT size)
+dwarf2out_args_size (HOST_WIDE_INT size)
 {
   dw_cfi_ref cfi;
 
@@ -1621,13 +1528,13 @@ dwarf2out_args_size (const char *label, 
   cfi = new_cfi ();
   cfi->dw_cfi_opc = DW_CFA_GNU_args_size;
   cfi->dw_cfi_oprnd1.dw_cfi_offset = size;
-  add_fde_cfi (label, cfi);
+  add_fde_cfi (cfi);
 }
 
 /* Record a stack adjustment of OFFSET bytes.  */
 
 static void
-dwarf2out_stack_adjust (HOST_WIDE_INT offset, const char *label)
+dwarf2out_stack_adjust (HOST_WIDE_INT offset)
 {
   if (cfa.reg == STACK_POINTER_REGNUM)
     cfa.offset += offset;
@@ -1646,9 +1553,9 @@ dwarf2out_stack_adjust (HOST_WIDE_INT of
   if (args_size < 0)
     args_size = 0;
 
-  def_cfa_1 (label, &cfa);
+  def_cfa_1 (false, &cfa);
   if (flag_asynchronous_unwind_tables)
-    dwarf2out_args_size (label, args_size);
+    dwarf2out_args_size (args_size);
 }
 
 /* Check INSN to see if it looks like a push or a stack adjustment, and
@@ -1659,7 +1566,6 @@ static void
 dwarf2out_notice_stack_adjust (rtx insn, bool after_p)
 {
   HOST_WIDE_INT offset;
-  const char *label;
   int i;
 
   /* Don't handle epilogues at all.  Certainly it would be wrong to do so
@@ -1690,7 +1596,7 @@ dwarf2out_notice_stack_adjust (rtx insn,
 	  if (GET_CODE (insn) == SET)
 	    insn = SET_SRC (insn);
 	  gcc_assert (GET_CODE (insn) == CALL);
-	  dwarf2out_args_size ("", INTVAL (XEXP (insn, 1)));
+	  dwarf2out_args_size (INTVAL (XEXP (insn, 1)));
 	}
       return;
     }
@@ -1698,7 +1604,7 @@ dwarf2out_notice_stack_adjust (rtx insn,
   if (CALL_P (insn) && !after_p)
     {
       if (!flag_asynchronous_unwind_tables)
-	dwarf2out_args_size ("", args_size);
+	dwarf2out_args_size (args_size);
       return;
     }
   else if (BARRIER_P (insn))
@@ -1739,8 +1645,7 @@ dwarf2out_notice_stack_adjust (rtx insn,
   if (offset == 0)
     return;
 
-  label = dwarf2out_cfi_label (false);
-  dwarf2out_stack_adjust (offset, label);
+  dwarf2out_stack_adjust (offset);
 }
 
 /* We delay emitting a register save until either (a) we reach the end
@@ -1769,13 +1674,11 @@ struct GTY(()) reg_saved_in_data {
 static GTY(()) struct reg_saved_in_data regs_saved_in_regs[4];
 static GTY(()) size_t num_regs_saved_in_regs;
 
-static const char *last_reg_save_label;
-
 /* Add an entry to QUEUED_REG_SAVES saying that REG is now saved at
    SREG, or if SREG is NULL then it is saved at OFFSET to the CFA.  */
 
 static void
-queue_reg_save (const char *label, rtx reg, rtx sreg, HOST_WIDE_INT offset)
+queue_reg_save (rtx reg, rtx sreg, HOST_WIDE_INT offset)
 {
   struct queued_reg_save *q;
 
@@ -1796,8 +1699,6 @@ queue_reg_save (const char *label, rtx r
   q->reg = reg;
   q->cfa_offset = offset;
   q->saved_reg = sreg;
-
-  last_reg_save_label = label;
 }
 
 /* Output all the entries in QUEUED_REG_SAVES.  */
@@ -1831,11 +1732,10 @@ dwarf2out_flush_queued_reg_saves (void)
 	sreg = DWARF_FRAME_REGNUM (REGNO (q->saved_reg));
       else
 	sreg = INVALID_REGNUM;
-      reg_save (last_reg_save_label, reg, sreg, q->cfa_offset);
+      reg_save (false, reg, sreg, q->cfa_offset);
     }
 
   queued_reg_saves = NULL;
-  last_reg_save_label = NULL;
 }
 
 /* Does INSN clobber any register which QUEUED_REG_SAVES lists a saved
@@ -1865,7 +1765,7 @@ clobbers_queued_reg_save (const_rtx insn
 /* Entry point for saving the first register into the second.  */
 
 void
-dwarf2out_reg_save_reg (const char *label, rtx reg, rtx sreg)
+dwarf2out_reg_save_reg (rtx reg, rtx sreg)
 {
   size_t i;
   unsigned int regno, sregno;
@@ -1883,7 +1783,7 @@ dwarf2out_reg_save_reg (const char *labe
 
   regno = DWARF_FRAME_REGNUM (REGNO (reg));
   sregno = DWARF_FRAME_REGNUM (REGNO (sreg));
-  reg_save (label, regno, sregno, 0);
+  reg_save (false, regno, sregno, 0);
 }
 
 /* What register, if any, is currently saved in REG?  */
@@ -1916,7 +1816,7 @@ static dw_cfa_location cfa_temp;
 /* A subroutine of dwarf2out_frame_debug, process a REG_DEF_CFA note.  */
 
 static void
-dwarf2out_frame_debug_def_cfa (rtx pat, const char *label)
+dwarf2out_frame_debug_def_cfa (rtx pat)
 {
   memset (&cfa, 0, sizeof (cfa));
 
@@ -1947,13 +1847,13 @@ dwarf2out_frame_debug_def_cfa (rtx pat, 
       gcc_unreachable ();
     }
 
-  def_cfa_1 (label, &cfa);
+  def_cfa_1 (false, &cfa);
 }
 
 /* A subroutine of dwarf2out_frame_debug, process a REG_ADJUST_CFA note.  */
 
 static void
-dwarf2out_frame_debug_adjust_cfa (rtx pat, const char *label)
+dwarf2out_frame_debug_adjust_cfa (rtx pat)
 {
   rtx src, dest;
 
@@ -1978,13 +1878,13 @@ dwarf2out_frame_debug_adjust_cfa (rtx pa
   cfa.reg = REGNO (dest);
   gcc_assert (cfa.indirect == 0);
 
-  def_cfa_1 (label, &cfa);
+  def_cfa_1 (false, &cfa);
 }
 
 /* A subroutine of dwarf2out_frame_debug, process a REG_CFA_OFFSET note.  */
 
 static void
-dwarf2out_frame_debug_cfa_offset (rtx set, const char *label)
+dwarf2out_frame_debug_cfa_offset (rtx set)
 {
   HOST_WIDE_INT offset;
   rtx src, addr, span;
@@ -2014,7 +1914,7 @@ dwarf2out_frame_debug_cfa_offset (rtx se
   /* ??? We'd like to use queue_reg_save, but we need to come up with
      a different flushing heuristic for epilogues.  */
   if (!span)
-    reg_save (label, DWARF_FRAME_REGNUM (REGNO (src)), INVALID_REGNUM, offset);
+    reg_save (false, DWARF_FRAME_REGNUM (REGNO (src)), INVALID_REGNUM, offset);
   else
     {
       /* We have a PARALLEL describing where the contents of SRC live.
@@ -2030,7 +1930,7 @@ dwarf2out_frame_debug_cfa_offset (rtx se
 	{
 	  rtx elem = XVECEXP (span, 0, par_index);
 
-	  reg_save (label, DWARF_FRAME_REGNUM (REGNO (elem)),
+	  reg_save (false, DWARF_FRAME_REGNUM (REGNO (elem)),
 		    INVALID_REGNUM, span_offset);
 	  span_offset += GET_MODE_SIZE (GET_MODE (elem));
 	}
@@ -2040,7 +1940,7 @@ dwarf2out_frame_debug_cfa_offset (rtx se
 /* A subroutine of dwarf2out_frame_debug, process a REG_CFA_REGISTER note.  */
 
 static void
-dwarf2out_frame_debug_cfa_register (rtx set, const char *label)
+dwarf2out_frame_debug_cfa_register (rtx set)
 {
   rtx src, dest;
   unsigned sregno, dregno;
@@ -2057,13 +1957,13 @@ dwarf2out_frame_debug_cfa_register (rtx 
 
   /* ??? We'd like to use queue_reg_save, but we need to come up with
      a different flushing heuristic for epilogues.  */
-  reg_save (label, sregno, dregno, 0);
+  reg_save (false, sregno, dregno, 0);
 }
 
 /* A subroutine of dwarf2out_frame_debug, process a REG_CFA_EXPRESSION note. */
 
 static void
-dwarf2out_frame_debug_cfa_expression (rtx set, const char *label)
+dwarf2out_frame_debug_cfa_expression (rtx set)
 {
   rtx src, dest, span;
   dw_cfi_ref cfi = new_cfi ();
@@ -2085,13 +1985,13 @@ dwarf2out_frame_debug_cfa_expression (rt
 
   /* ??? We'd like to use queue_reg_save, were the interface different,
      and, as above, we could manage flushing for epilogues.  */
-  add_fde_cfi (label, cfi);
+  add_fde_cfi (cfi);
 }
 
 /* A subroutine of dwarf2out_frame_debug, process a REG_CFA_RESTORE note.  */
 
 static void
-dwarf2out_frame_debug_cfa_restore (rtx reg, const char *label)
+dwarf2out_frame_debug_cfa_restore (rtx reg)
 {
   dw_cfi_ref cfi = new_cfi ();
   unsigned int regno = DWARF_FRAME_REGNUM (REGNO (reg));
@@ -2099,7 +1999,102 @@ dwarf2out_frame_debug_cfa_restore (rtx r
   cfi->dw_cfi_opc = (regno & ~0x3f ? DW_CFA_restore_extended : DW_CFA_restore);
   cfi->dw_cfi_oprnd1.dw_cfi_reg_num = regno;
 
-  add_fde_cfi (label, cfi);
+  add_fde_cfi (cfi);
+}
+
+/* Examine CFI and return true if a cfi label and set_loc is needed before
+   it.  Even when generating CFI assembler instructions, we still have to
+   add the cfi to the list so that lookup_cfa works later on.  When
+   -g2 and above we even need to force emitting of CFI labels and add
+   to list a DW_CFA_set_loc for convert_cfa_to_fb_loc_list purposes.
+   If we're generating DWARF3 output we use DW_OP_call_frame_cfa and
+   so don't use convert_cfa_to_fb_loc_list.  */
+
+static bool
+cfi_label_required_p (dw_cfi_ref cfi)
+{
+  if (!dwarf2out_do_cfi_asm ())
+    return true;
+
+  if (dwarf_version == 2
+      && debug_info_level > DINFO_LEVEL_TERSE
+      && (write_symbols == DWARF2_DEBUG
+	  || write_symbols == VMS_AND_DWARF2_DEBUG))
+    {
+      switch (cfi->dw_cfi_opc)
+	{
+	case DW_CFA_def_cfa_offset:
+	case DW_CFA_def_cfa_offset_sf:
+	case DW_CFA_def_cfa_register:
+	case DW_CFA_def_cfa:
+	case DW_CFA_def_cfa_sf:
+	case DW_CFA_def_cfa_expression:
+	case DW_CFA_restore_state:
+	  return true;
+	default:
+	  return false;
+	}
+    }
+  return false;
+}
+
+/* Walk the functino, looking for NOTE_INSN_CFI notes.  Add the CFIs to the
+   function's FDE, adding CFI labels and set_loc/advance_loc opcodes as
+   necessary.  */
+static void
+add_cfis_to_fde (void)
+{
+  dw_fde_ref fde = current_fde ();
+  rtx 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)
+	/* Don't attempt to advance_loc4 between labels in different
+	   sections.  */
+	first = true;
+
+      if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_CFI)
+	{
+	  bool required = cfi_label_required_p (NOTE_CFI (insn));
+	  while (next && NOTE_P (next) && NOTE_KIND (next) == NOTE_INSN_CFI)
+	    {
+	      required |= cfi_label_required_p (NOTE_CFI (next));
+	      next = NEXT_INSN (next);
+	    }
+	  if (required)
+	    {
+	      int num = dwarf2out_cfi_label_num;
+	      const char *label = dwarf2out_cfi_label ();
+	      dw_cfi_ref xcfi;
+	      rtx tmp;
+
+	      label = xstrdup (label);
+
+	      /* 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_oprnd1.dw_cfi_addr = label;
+	      VEC_safe_push (dw_cfi_ref, gc, fde->dw_fde_cfi, xcfi);
+
+	      tmp = emit_note_before (NOTE_INSN_CFI_LABEL, insn);
+	      NOTE_LABEL_NUMBER (tmp) = num;
+	    }
+
+	  do
+	    {
+	      VEC_safe_push (dw_cfi_ref, gc, fde->dw_fde_cfi, NOTE_CFI (insn));
+	      insn = NEXT_INSN (insn);
+	    }
+	  while (insn != next);
+	  first = false;
+	}
+    }
 }
 
 /* Record call frame debugging information for an expression EXPR,
@@ -2298,7 +2293,7 @@ dwarf2out_frame_debug_cfa_restore (rtx r
   	   cfa.reg == fde->drap_reg  */
 
 static void
-dwarf2out_frame_debug_expr (rtx expr, const char *label)
+dwarf2out_frame_debug_expr (rtx expr)
 {
   rtx src, dest, span;
   HOST_WIDE_INT offset;
@@ -2327,7 +2322,7 @@ dwarf2out_frame_debug_expr (rtx expr, co
 	    if (GET_CODE (elem) == SET
 		&& MEM_P (SET_DEST (elem))
 		&& (RTX_FRAME_RELATED_P (elem) || par_index == 0))
-	      dwarf2out_frame_debug_expr (elem, label);
+	      dwarf2out_frame_debug_expr (elem);
 	  }
 
       for (par_index = 0; par_index < limit; par_index++)
@@ -2336,7 +2331,7 @@ dwarf2out_frame_debug_expr (rtx expr, co
 	  if (GET_CODE (elem) == SET
 	      && (!MEM_P (SET_DEST (elem)) || GET_CODE (expr) == SEQUENCE)
 	      && (RTX_FRAME_RELATED_P (elem) || par_index == 0))
-	    dwarf2out_frame_debug_expr (elem, label);
+	    dwarf2out_frame_debug_expr (elem);
 	  else if (GET_CODE (elem) == SET
 		   && par_index != 0
 		   && !RTX_FRAME_RELATED_P (elem))
@@ -2346,7 +2341,7 @@ dwarf2out_frame_debug_expr (rtx expr, co
 	      HOST_WIDE_INT offset = stack_adjust_offset (elem, args_size, 0);
 
 	      if (offset != 0)
-		dwarf2out_stack_adjust (offset, label);
+		dwarf2out_stack_adjust (offset);
 	    }
 	}
       return;
@@ -2406,7 +2401,7 @@ dwarf2out_frame_debug_expr (rtx expr, co
 			    && fde->drap_reg != INVALID_REGNUM
 			    && cfa.reg != REGNO (src));
 	      else
-		queue_reg_save (label, src, dest, 0);
+		queue_reg_save (src, dest, 0);
 	    }
 	  break;
 
@@ -2536,7 +2531,7 @@ dwarf2out_frame_debug_expr (rtx expr, co
 	case UNSPEC:
 	case UNSPEC_VOLATILE:
 	  gcc_assert (targetm.dwarf_handle_frame_unspec);
-	  targetm.dwarf_handle_frame_unspec (label, expr, XINT (src, 1));
+	  targetm.dwarf_handle_frame_unspec (expr, XINT (src, 1));
 	  return;
 
 	  /* Rule 16 */
@@ -2565,7 +2560,7 @@ dwarf2out_frame_debug_expr (rtx expr, co
 	  gcc_unreachable ();
 	}
 
-      def_cfa_1 (label, &cfa);
+      def_cfa_1 (false, &cfa);
       break;
 
     case MEM:
@@ -2721,15 +2716,15 @@ dwarf2out_frame_debug_expr (rtx expr, co
 
 		  fde->drap_reg_saved = 1;
 
-		  def_cfa_1 (label, &cfa_exp);
+		  def_cfa_1 (false, &cfa_exp);
 		  break;
                 }
 
 	      /* If the source register is exactly the CFA, assume
 		 we're saving SP like any other register; this happens
 		 on the ARM.  */
-	      def_cfa_1 (label, &cfa);
-	      queue_reg_save (label, stack_pointer_rtx, NULL_RTX, offset);
+	      def_cfa_1 (false, &cfa);
+	      queue_reg_save (stack_pointer_rtx, NULL_RTX, offset);
 	      break;
 	    }
 	  else
@@ -2745,17 +2740,17 @@ dwarf2out_frame_debug_expr (rtx expr, co
 	      cfa.reg = REGNO (x);
 	      cfa.base_offset = offset;
 	      cfa.indirect = 1;
-	      def_cfa_1 (label, &cfa);
+	      def_cfa_1 (false, &cfa);
 	      break;
 	    }
 	}
 
-      def_cfa_1 (label, &cfa);
+      def_cfa_1 (false, &cfa);
       {
 	span = targetm.dwarf_register_span (src);
 
 	if (!span)
-	  queue_reg_save (label, src, NULL_RTX, offset);
+	  queue_reg_save (src, NULL_RTX, offset);
 	else
 	  {
 	    /* We have a PARALLEL describing where the contents of SRC
@@ -2772,7 +2767,7 @@ dwarf2out_frame_debug_expr (rtx expr, co
 	      {
 		rtx elem = XVECEXP (span, 0, par_index);
 
-		queue_reg_save (label, elem, NULL_RTX, span_offset);
+		queue_reg_save (elem, NULL_RTX, span_offset);
 		span_offset += GET_MODE_SIZE (GET_MODE (elem));
 	      }
 	  }
@@ -2794,7 +2789,6 @@ dwarf2out_frame_debug_expr (rtx expr, co
 void
 dwarf2out_frame_debug (rtx insn, bool after_p)
 {
-  const char *label;
   rtx note, n;
   bool handled_one = false;
 
@@ -2813,10 +2807,10 @@ dwarf2out_frame_debug (rtx insn, bool af
 	 is still used to save registers.  */
       if (!ACCUMULATE_OUTGOING_ARGS)
 	dwarf2out_notice_stack_adjust (insn, after_p);
+      cfi_insn = NULL;
       return;
     }
 
-  label = dwarf2out_cfi_label (false);
   any_cfis_emitted = false;
 
   for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
@@ -2827,7 +2821,7 @@ dwarf2out_frame_debug (rtx insn, bool af
 	goto found;
 
       case REG_CFA_DEF_CFA:
-	dwarf2out_frame_debug_def_cfa (XEXP (note, 0), label);
+	dwarf2out_frame_debug_def_cfa (XEXP (note, 0));
 	handled_one = true;
 	break;
 
@@ -2839,7 +2833,7 @@ dwarf2out_frame_debug (rtx insn, bool af
 	    if (GET_CODE (n) == PARALLEL)
 	      n = XVECEXP (n, 0, 0);
 	  }
-	dwarf2out_frame_debug_adjust_cfa (n, label);
+	dwarf2out_frame_debug_adjust_cfa (n);
 	handled_one = true;
 	break;
 
@@ -2847,7 +2841,7 @@ dwarf2out_frame_debug (rtx insn, bool af
 	n = XEXP (note, 0);
 	if (n == NULL)
 	  n = single_set (insn);
-	dwarf2out_frame_debug_cfa_offset (n, label);
+	dwarf2out_frame_debug_cfa_offset (n);
 	handled_one = true;
 	break;
 
@@ -2859,7 +2853,7 @@ dwarf2out_frame_debug (rtx insn, bool af
 	    if (GET_CODE (n) == PARALLEL)
 	      n = XVECEXP (n, 0, 0);
 	  }
-	dwarf2out_frame_debug_cfa_register (n, label);
+	dwarf2out_frame_debug_cfa_register (n);
 	handled_one = true;
 	break;
 
@@ -2867,7 +2861,7 @@ dwarf2out_frame_debug (rtx insn, bool af
 	n = XEXP (note, 0);
 	if (n == NULL)
 	  n = single_set (insn);
-	dwarf2out_frame_debug_cfa_expression (n, label);
+	dwarf2out_frame_debug_cfa_expression (n);
 	handled_one = true;
 	break;
 
@@ -2880,7 +2874,7 @@ dwarf2out_frame_debug (rtx insn, bool af
 	      n = XVECEXP (n, 0, 0);
 	    n = XEXP (n, 0);
 	  }
-	dwarf2out_frame_debug_cfa_restore (n, label);
+	dwarf2out_frame_debug_cfa_restore (n);
 	handled_one = true;
 	break;
 
@@ -2906,18 +2900,20 @@ dwarf2out_frame_debug (rtx insn, bool af
     {
       if (any_cfis_emitted)
 	dwarf2out_flush_queued_reg_saves ();
+      cfi_insn = NULL;
       return;
     }
 
   insn = PATTERN (insn);
  found:
-  dwarf2out_frame_debug_expr (insn, label);
+  dwarf2out_frame_debug_expr (insn);
 
   /* 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 above.  */
   if (any_cfis_emitted || clobbers_queued_reg_save (insn))
     dwarf2out_flush_queued_reg_saves ();
+  cfi_insn = NULL;
 }
 
 /* Called once at the start of final to initialize some data for the
@@ -2926,7 +2922,6 @@ void
 dwarf2out_frame_debug_init (void)
 {
   size_t i;
-  rtx insn;
 
   /* Flush any queued register saves.  */
   dwarf2out_flush_queued_reg_saves ();
@@ -2936,6 +2931,7 @@ dwarf2out_frame_debug_init (void)
   gcc_assert (cfa.reg
 	      == (unsigned long)DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM));
 
+  old_cfa = cfa;
   cfa.reg = STACK_POINTER_REGNUM;
   cfa_store = cfa;
   cfa_temp.reg = -1;
@@ -2947,7 +2943,15 @@ dwarf2out_frame_debug_init (void)
       regs_saved_in_regs[i].saved_in_reg = NULL_RTX;
     }
   num_regs_saved_in_regs = 0;
+}
+
+/* After the (optional) text prologue has been written, emit CFI insns
+   and update the FDE for frame-related instructions.  */
 
+void
+dwarf2out_frame_debug_after_prologue (void)
+{
+  rtx insn;
   if (barrier_args_size)
     {
       XDELETEVEC (barrier_args_size);
@@ -2973,6 +2977,7 @@ dwarf2out_frame_debug_init (void)
 	    case NOTE_INSN_CFA_RESTORE_STATE:
 	      cfi_insn = insn;
 	      dwarf2out_frame_debug_restore_state ();
+	      cfi_insn = NULL;
 	      break;
 	    }
 	  continue;
@@ -2999,12 +3004,15 @@ dwarf2out_frame_debug_init (void)
 	  )
 	dwarf2out_frame_debug (insn, true);
     }
+
+  add_cfis_to_fde ();
 }
 
 void
 dwarf2out_emit_cfi (dw_cfi_ref cfi)
 {
-  output_cfi_directive (cfi);
+  if (dwarf2out_do_cfi_asm ())
+    output_cfi_directive (cfi);
 }
 
 /* Determine if we need to save and restore CFI information around
@@ -3085,23 +3093,24 @@ dwarf2out_cfi_begin_epilogue (rtx insn)
   /* And emulate the state save.  */
   gcc_assert (!cfa_remember.in_use);
   cfa_remember = cfa;
+  old_cfa_remember = old_cfa;
   cfa_remember.in_use = 1;
 }
 
 /* A "subroutine" of dwarf2out_cfi_begin_epilogue.  Emit the restore
    required.  */
 
-void
+static void
 dwarf2out_frame_debug_restore_state (void)
 {
   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);
+  add_fde_cfi (cfi);
 
   gcc_assert (cfa_remember.in_use);
   cfa = cfa_remember;
+  old_cfa = old_cfa_remember;
   cfa_remember.in_use = 0;
 }
 
@@ -4296,7 +4305,8 @@ dwarf2out_frame_init (void)
      sake of lookup_cfa.  */
 
   /* On entry, the Canonical Frame Address is at SP.  */
-  dwarf2out_def_cfa (NULL, STACK_POINTER_REGNUM, INCOMING_FRAME_SP_OFFSET);
+  old_cfa.reg = INVALID_REGNUM;
+  dwarf2out_def_cfa (true, STACK_POINTER_REGNUM, INCOMING_FRAME_SP_OFFSET);
 
   if (targetm.debug_unwind_info () == UI_DWARF2
       || targetm.except_unwind_info (&global_options) == UI_DWARF2)
@@ -4353,10 +4363,6 @@ dwarf2out_switch_text_section (void)
     }
   have_multiple_function_sections = true;
 
-  /* Reset the current label on switching text sections, so that we
-     don't attempt to advance_loc4 between labels in different sections.  */
-  fde->dw_fde_current_label = NULL;
-
   /* There is no need to mark used sections when not debugging.  */
   if (cold_text_section != NULL)
     dwarf2out_note_section_used ();
Index: gcc/dwarf2out.h
===================================================================
--- gcc.orig/dwarf2out.h
+++ gcc/dwarf2out.h
@@ -19,9 +19,41 @@ along with GCC; see the file COPYING3.  
 <http://www.gnu.org/licenses/>.  */
 
 struct dw_cfi_struct;
+/* In dwarf2out.c */
+/* Interface of the DWARF2 unwind info support.  */
+
+/* Generate a new label for the CFI info to refer to.  */
+
+extern void dwarf2out_maybe_emit_cfi_label (void);
+
+/* Entry point to update the canonical frame address (CFA).  */
+
+extern void dwarf2out_def_cfa (bool, unsigned, HOST_WIDE_INT);
+
+/* Add the CFI for saving a register window.  */
+
+extern void dwarf2out_window_save (void);
+
+/* Entry point for saving a register to the stack.  */
+
+extern void dwarf2out_reg_save (unsigned, HOST_WIDE_INT);
+
+/* Entry point for saving the return address in the stack.  */
+
+extern void dwarf2out_return_save (HOST_WIDE_INT);
+
+/* Entry point for saving the return address in a register.  */
+
+extern void dwarf2out_return_reg (unsigned);
+
+/* Entry point for saving the first register into the second.  */
+
+extern void dwarf2out_reg_save_reg (rtx, rtx);
+
 extern void dwarf2out_decl (tree);
 extern void dwarf2out_frame_debug (rtx, bool);
 extern void dwarf2out_frame_debug_init (void);
+extern void dwarf2out_frame_debug_after_prologue (void);
 extern void dwarf2out_emit_cfi (struct dw_cfi_struct *);
 extern void dwarf2out_flush_queued_reg_saves (void);
 
Index: gcc/final.c
===================================================================
--- gcc.orig/final.c
+++ gcc/final.c
@@ -1588,6 +1588,11 @@ final_start_function (rtx first ATTRIBUT
   /* First output the function prologue: code to set up the stack frame.  */
   targetm.asm_out.function_prologue (file, get_frame_size ());
 
+#if defined (HAVE_prologue)
+  if (dwarf2out_do_frame ())
+    dwarf2out_frame_debug_after_prologue ();
+#endif
+
   /* If the machine represents the prologue as RTL, the profiling code must
      be emitted when NOTE_INSN_PROLOGUE_END is scanned.  */
 #ifdef HAVE_prologue
Index: gcc/target.def
===================================================================
--- gcc.orig/target.def
+++ gcc/target.def
@@ -1792,7 +1792,7 @@ DEFHOOK
 DEFHOOK
 (dwarf_handle_frame_unspec,
  "",
- void, (const char *label, rtx pattern, int index), NULL)
+ void, (rtx pattern, int index), NULL)
 
 /* ??? Documenting this hook requires a GFDL license grant.  */
 DEFHOOK_UNDOC
Index: gcc/tree.h
===================================================================
--- gcc.orig/tree.h
+++ gcc/tree.h
@@ -5424,37 +5424,6 @@ extern tree tree_overlaps_hard_reg_set (
 #endif
 
 
-/* In dwarf2out.c */
-/* Interface of the DWARF2 unwind info support.  */
-
-/* Generate a new label for the CFI info to refer to.  */
-
-extern char *dwarf2out_cfi_label (bool);
-
-/* Entry point to update the canonical frame address (CFA).  */
-
-extern void dwarf2out_def_cfa (const char *, unsigned, HOST_WIDE_INT);
-
-/* Add the CFI for saving a register window.  */
-
-extern void dwarf2out_window_save (const char *);
-
-/* Entry point for saving a register to the stack.  */
-
-extern void dwarf2out_reg_save (const char *, unsigned, HOST_WIDE_INT);
-
-/* Entry point for saving the return address in the stack.  */
-
-extern void dwarf2out_return_save (const char *, HOST_WIDE_INT);
-
-/* Entry point for saving the return address in a register.  */
-
-extern void dwarf2out_return_reg (const char *, unsigned);
-
-/* Entry point for saving the first register into the second.  */
-
-extern void dwarf2out_reg_save_reg (const char *, rtx, rtx);
-
 /* In tree-inline.c  */
 
 /* The type of a set of already-visited pointers.  Functions for creating
Index: gcc/doc/tm.texi
===================================================================
--- gcc.orig/doc/tm.texi
+++ gcc/doc/tm.texi
@@ -3203,7 +3203,7 @@ someone decided it was a good idea to us
 terminate the stack backtrace.  New ports should avoid this.
 @end defmac
 
-@deftypefn {Target Hook} void TARGET_DWARF_HANDLE_FRAME_UNSPEC (const char *@var{label}, rtx @var{pattern}, int @var{index})
+@deftypefn {Target Hook} void TARGET_DWARF_HANDLE_FRAME_UNSPEC (rtx @var{pattern}, int @var{index})
 This target hook allows the backend to emit frame-related insns that
 contain UNSPECs or UNSPEC_VOLATILEs.  The DWARF 2 call frame debugging
 info engine will invoke it on insns of the form
Index: gcc/dwarf2out.c
===================================================================
--- gcc.orig/dwarf2out.c
+++ gcc/dwarf2out.c
@@ -465,12 +465,11 @@ 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);
-static void dwarf2out_cfi_begin_epilogue (rtx);
 static void dwarf2out_frame_debug_restore_state (void);
 
 /* Support for complex CFA locations.  */
@@ -823,9 +822,6 @@ new_cfi (void)
 /* 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;
-
 /* True if any CFI directives were emitted at the current insn.  */
 static bool any_cfis_emitted;
 
@@ -868,28 +864,34 @@ dwarf2out_maybe_emit_cfi_label (void)
     }
 }
 
+static void
+add_cfa_remember (void)
+{
+  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 (cfi_remember);
+}
+
+/* Nonnull if add_fde_cfi should not just emit a NOTE_INSN_CFI, but
+   also add the CFI to this vector.  */
+static cfi_vec *cfi_insn_vec;
+
 /* Add CFI to the current fde at the PC value indicated by LABEL if specified,
    or to the CIE if LABEL is NULL.  */
 
 static void
 add_fde_cfi (dw_cfi_ref cfi)
 {
-  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 (cfi_remember);
-    }
-
   any_cfis_emitted = true;
   if (cfi_insn != NULL)
     {
       cfi_insn = emit_note_after (NOTE_INSN_CFI, cfi_insn);
       NOTE_CFI (cfi_insn) = cfi;
+      if (cfi_insn_vec != NULL)
+	VEC_safe_push (dw_cfi_ref, gc, *cfi_insn_vec, cfi);
     }
   else
     {
@@ -980,12 +982,6 @@ static dw_cfa_location old_cfa;
    from the CFA.  */
 static dw_cfa_location cfa_store;
 
-/* The current save location around an epilogue.  */
-static dw_cfa_location cfa_remember;
-
-/* Like cfa_remember, but a copy of old_cfa.  */
-static dw_cfa_location old_cfa_remember;
-
 /* The running total of the size of arguments pushed onto the stack.  */
 static HOST_WIDE_INT args_size;
 
@@ -1339,179 +1335,6 @@ stack_adjust_offset (const_rtx pattern, 
   return offset;
 }
 
-/* Precomputed args_size for CODE_LABELs and BARRIERs preceeding them,
-   indexed by INSN_UID.  */
-
-static HOST_WIDE_INT *barrier_args_size;
-
-/* Helper function for compute_barrier_args_size.  Handle one insn.  */
-
-static HOST_WIDE_INT
-compute_barrier_args_size_1 (rtx insn, HOST_WIDE_INT cur_args_size,
-			     VEC (rtx, heap) **next)
-{
-  HOST_WIDE_INT offset = 0;
-  int i;
-
-  if (! RTX_FRAME_RELATED_P (insn))
-    {
-      if (prologue_epilogue_contains (insn))
-	/* Nothing */;
-      else if (GET_CODE (PATTERN (insn)) == SET)
-	offset = stack_adjust_offset (PATTERN (insn), cur_args_size, 0);
-      else if (GET_CODE (PATTERN (insn)) == PARALLEL
-	       || GET_CODE (PATTERN (insn)) == SEQUENCE)
-	{
-	  /* There may be stack adjustments inside compound insns.  Search
-	     for them.  */
-	  for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
-	    if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET)
-	      offset += stack_adjust_offset (XVECEXP (PATTERN (insn), 0, i),
-					     cur_args_size, offset);
-	}
-    }
-  else
-    {
-      rtx expr = find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX);
-
-      if (expr)
-	{
-	  expr = XEXP (expr, 0);
-	  if (GET_CODE (expr) == PARALLEL
-	      || GET_CODE (expr) == SEQUENCE)
-	    for (i = 1; i < XVECLEN (expr, 0); i++)
-	      {
-		rtx elem = XVECEXP (expr, 0, i);
-
-		if (GET_CODE (elem) == SET && !RTX_FRAME_RELATED_P (elem))
-		  offset += stack_adjust_offset (elem, cur_args_size, offset);
-	      }
-	}
-    }
-
-#ifndef STACK_GROWS_DOWNWARD
-  offset = -offset;
-#endif
-
-  cur_args_size += offset;
-  if (cur_args_size < 0)
-    cur_args_size = 0;
-
-  if (JUMP_P (insn))
-    {
-      rtx dest = JUMP_LABEL (insn);
-
-      if (dest)
-	{
-	  if (barrier_args_size [INSN_UID (dest)] < 0)
-	    {
-	      barrier_args_size [INSN_UID (dest)] = cur_args_size;
-	      VEC_safe_push (rtx, heap, *next, dest);
-	    }
-	}
-    }
-
-  return cur_args_size;
-}
-
-/* Walk the whole function and compute args_size on BARRIERs.  */
-
-static void
-compute_barrier_args_size (void)
-{
-  int max_uid = get_max_uid (), i;
-  rtx insn;
-  VEC (rtx, heap) *worklist, *next, *tmp;
-
-  barrier_args_size = XNEWVEC (HOST_WIDE_INT, max_uid);
-  for (i = 0; i < max_uid; i++)
-    barrier_args_size[i] = -1;
-
-  worklist = VEC_alloc (rtx, heap, 20);
-  next = VEC_alloc (rtx, heap, 20);
-  insn = get_insns ();
-  barrier_args_size[INSN_UID (insn)] = 0;
-  VEC_quick_push (rtx, worklist, insn);
-  for (;;)
-    {
-      while (!VEC_empty (rtx, worklist))
-	{
-	  rtx prev, body, first_insn;
-	  HOST_WIDE_INT cur_args_size;
-
-	  first_insn = insn = VEC_pop (rtx, worklist);
-	  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;
-
-	  for (; insn; insn = NEXT_INSN (insn))
-	    {
-	      if (INSN_DELETED_P (insn) || NOTE_P (insn))
-		continue;
-	      if (BARRIER_P (insn))
-		break;
-
-	      if (LABEL_P (insn))
-		{
-		  if (insn == first_insn)
-		    continue;
-		  else if (barrier_args_size[INSN_UID (insn)] < 0)
-		    {
-		      barrier_args_size[INSN_UID (insn)] = cur_args_size;
-		      continue;
-		    }
-		  else
-		    {
-		      /* The insns starting with this label have been
-			 already scanned or are in the worklist.  */
-		      break;
-		    }
-		}
-
-	      body = PATTERN (insn);
-	      if (GET_CODE (body) == SEQUENCE)
-		{
-		  HOST_WIDE_INT dest_args_size = cur_args_size;
-		  for (i = 1; i < XVECLEN (body, 0); i++)
-		    if (INSN_ANNULLED_BRANCH_P (XVECEXP (body, 0, 0))
-			&& INSN_FROM_TARGET_P (XVECEXP (body, 0, i)))
-		      dest_args_size
-			= compute_barrier_args_size_1 (XVECEXP (body, 0, i),
-						       dest_args_size, &next);
-		    else
-		      cur_args_size
-			= compute_barrier_args_size_1 (XVECEXP (body, 0, i),
-						       cur_args_size, &next);
-
-		  if (INSN_ANNULLED_BRANCH_P (XVECEXP (body, 0, 0)))
-		    compute_barrier_args_size_1 (XVECEXP (body, 0, 0),
-						 dest_args_size, &next);
-		  else
-		    cur_args_size
-		      = compute_barrier_args_size_1 (XVECEXP (body, 0, 0),
-						     cur_args_size, &next);
-		}
-	      else
-		cur_args_size
-		  = compute_barrier_args_size_1 (insn, cur_args_size, &next);
-	    }
-	}
-
-      if (VEC_empty (rtx, next))
-	break;
-
-      /* Swap WORKLIST with NEXT and truncate NEXT for next iteration.  */
-      tmp = next;
-      next = worklist;
-      worklist = tmp;
-      VEC_truncate (rtx, next, 0);
-    }
-
-  VEC_free (rtx, heap, worklist);
-  VEC_free (rtx, heap, next);
-}
-
 /* Add a CFI to update the running total of the size of arguments
    pushed onto the stack.  */
 
@@ -1608,25 +1431,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
@@ -2054,9 +1859,12 @@ add_cfis_to_fde (void)
       next = NEXT_INSN (insn);
 
       if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS)
-	/* Don't attempt to advance_loc4 between labels in different
-	   sections.  */
-	first = true;
+	{
+	  fde->dw_fde_switch_cfi_index = VEC_length (dw_cfi_ref, fde->dw_fde_cfi);
+	  /* Don't attempt to advance_loc4 between labels in different
+	     sections.  */
+	  first = true;
+	}
 
       if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_CFI)
 	{
@@ -2097,6 +1905,17 @@ add_cfis_to_fde (void)
     }
 }
 
+/* A subroutine of dwarf2out_frame_debug_init, emit a CFA_restore_state.  */
+
+void
+dwarf2out_frame_debug_restore_state (void)
+{
+  dw_cfi_ref cfi = new_cfi ();
+
+  cfi->dw_cfi_opc = DW_CFA_restore_state;
+  add_fde_cfi (cfi);
+}
+
 /* Record call frame debugging information for an expression EXPR,
    which either sets SP or FP (adjusting how we calculate the frame
    address) or saves a register to the stack or another register.
@@ -2797,9 +2616,6 @@ dwarf2out_frame_debug (rtx insn, bool af
   else
     cfi_insn = PREV_INSN (insn);
 
-  if (!NONJUMP_INSN_P (insn) || clobbers_queued_reg_save (insn))
-    dwarf2out_flush_queued_reg_saves ();
-
   if (!RTX_FRAME_RELATED_P (insn))
     {
       /* ??? This should be done unconditionally since stack adjustments
@@ -2945,56 +2761,224 @@ dwarf2out_frame_debug_init (void)
   num_regs_saved_in_regs = 0;
 }
 
-/* After the (optional) text prologue has been written, emit CFI insns
-   and update the FDE for frame-related instructions.  */
+/* Copy a CFI vector, except for args_size opcodes.  */
+static cfi_vec
+copy_cfi_vec_parts (cfi_vec in_vec)
+{
+  int length = VEC_length (dw_cfi_ref, in_vec);
+  /* Ensure we always have a pointer to a vector, not just NULL.  */
+  cfi_vec new_vec = VEC_alloc (dw_cfi_ref, gc, length > 0 ? length : 1);
+  int i;
+  for (i = 0; i < length; i++)
+    {
+      dw_cfi_ref elt = VEC_index (dw_cfi_ref, in_vec, i);
+      if (elt->dw_cfi_opc == DW_CFA_GNU_args_size)
+	continue;
 
-void
-dwarf2out_frame_debug_after_prologue (void)
+      VEC_quick_push (dw_cfi_ref, new_vec, elt);
+    }
+  return new_vec;
+}
+
+/* Record the state of the CFI program at a point in the program.  */
+typedef struct
 {
-  rtx insn;
-  if (barrier_args_size)
+  /* The CFI instructions up to this point.  */
+  cfi_vec cfis;
+  /* Copies of the global variables with the same name.  */
+  dw_cfa_location cfa, cfa_store, old_cfa;
+  /* True if we have seen this point during a scan in scan_until_barrier.  */
+  bool visited;
+  /* True if this point was used as a starting point for such a scan.  */
+  bool used_as_start;
+  /* Other than CFI instructions and CFA state, the only thing necessary to
+     be tracked is the argument size.  */
+  int args_size;
+  /* Nonzero for states that must be remembered and restored.  If higher
+     than one, the first restores will be immediately followed by another
+     remember.  */
+  int n_restores;
+} jump_target_info;
+
+/* Return true if we'll want to save or restore CFI state at INSN.  This is
+   true for labels and barriers, and certain notes.  */
+static bool
+save_point_p (rtx insn)
+{
+  return (BARRIER_P (insn) || LABEL_P (insn)
+	  || (NOTE_P (insn)
+	      && (NOTE_KIND (insn) == NOTE_INSN_FUNCTION_BEG
+		  || NOTE_KIND (insn) == NOTE_INSN_PROLOGUE_END
+		  || NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS)));
+}
+
+/* Save the current state in INFO.  */
+
+static void
+record_current_state (jump_target_info *info)
+{
+  info->cfis = copy_cfi_vec_parts (*cfi_insn_vec);
+  info->args_size = args_size;
+  info->cfa = cfa;
+  info->old_cfa = old_cfa;
+  info->cfa_store = cfa_store;
+}
+
+/* LABEL is the target of a jump we encountered while scanning the
+   function.  Record it in START_POINTS as a potential new starting point
+   for the scan, unless we've visited it before.  UID_LUID gives a
+   mapping for uids used to index INFO, which holds the CFI
+   information for labels and barriers.  */
+static void
+maybe_record_jump_target (rtx label, VEC (rtx, heap) **start_points,
+			  int *uid_luid, jump_target_info *info)
+{
+  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, *start_points, label);
+  record_current_state (info);
+}
+
+/* Return true if VEC1 and VEC2 are identical up to the length of VEC1.  */
+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;
+}
+
+/* Append entries to FDE's cfi vector.  PREFIX and FULL are two
+   existing vectors, where PREFIX is contained in FULL as a prefix.  */
+
+static void
+append_extra_cfis (cfi_vec prefix, cfi_vec full)
+{
+  int i;
+  int len = VEC_length (dw_cfi_ref, full);
+  int prefix_len = VEC_length (dw_cfi_ref, prefix);
+
+  gcc_assert (prefix_len <= len);
+  for (i = 0; i < prefix_len; i++)
     {
-      XDELETEVEC (barrier_args_size);
-      barrier_args_size = NULL;
+      dw_cfi_ref elt, elt2;
+
+      elt = VEC_index (dw_cfi_ref, full, i);
+      elt2 = VEC_index (dw_cfi_ref, prefix, i);
+      gcc_assert (elt == elt2);
     }
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+  for (; i < len; i++)
     {
-      rtx pat;
-      if (BARRIER_P (insn))
-	{
-	  dwarf2out_frame_debug (insn, false);
-	  continue;
-	}
-      else if (NOTE_P (insn))
+      dw_cfi_ref elt = VEC_index (dw_cfi_ref, full, i);
+      add_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);
+}
+
+static bool
+switch_note_p (rtx insn)
+{
+  return NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS;
+}
+
+/* From the current starting point in INSN, scan forwards until we hit a
+   barrier, the end of the function, or a label we've previously used as
+   a starting point.
+   UID_LUID is a mapping to linear uids used to map an insn to an entry in
+   POINT_INFO, if save_point_p is true for a given insn.  */
+
+static void
+scan_until_barrier (rtx insn, jump_target_info *point_info, int *uid_luid,
+		    VEC (rtx, heap) **start_points)
+{
+  rtx next;
+  for (; insn != NULL_RTX; insn = next)
+    {
+      int uid = INSN_UID (insn);
+      rtx pat, note;
+
+      next = NEXT_INSN (insn);
+      if (save_point_p (insn))
 	{
-	  switch (NOTE_KIND (insn))
-	    {
-	    case NOTE_INSN_EPILOGUE_BEG:
-#if defined (HAVE_epilogue)
-	      dwarf2out_cfi_begin_epilogue (insn);
-#endif
+	  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;
-	    case NOTE_INSN_CFA_RESTORE_STATE:
-	      cfi_insn = insn;
-	      dwarf2out_frame_debug_restore_state ();
-	      cfi_insn = NULL;
+	    }
+	  info->visited = true;
+	  if (BARRIER_P (insn))
+	    gcc_assert (info->cfis == NULL);
+	  if (switch_note_p (insn))
+	    {
+	      /* Don't record the state, it was set to a clean slate in
+		 the caller.  */
+	      if (dump_file)
+		fprintf (dump_file,
+			 "Stopping scan at text section switch %d\n", uid);
+	      break;
+	    }
+	  record_current_state (info);
+	  if (BARRIER_P (insn))
+	    {
+	      if (dump_file)
+		fprintf (dump_file, "Stopping scan at barrier %d\n", uid);
 	      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);
+	  int i;
+	  for (i = 1; i < XVECLEN (pat, 0); i++)
+	    dwarf2out_frame_debug (XVECEXP (pat, 0, i), false);
 	  insn = XVECEXP (pat, 0, 0);
 	}
 
+      if (!NONJUMP_INSN_P (insn) || clobbers_queued_reg_save (insn)
+	  || (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_PROLOGUE_END))
+	{
+	  cfi_insn = PREV_INSN (insn);
+	  dwarf2out_flush_queued_reg_saves ();
+	  cfi_insn = NULL_RTX;
+	}
+
       if (CALL_P (insn) && dwarf2out_do_frame ())
 	dwarf2out_frame_debug (insn, false);
       if (dwarf2out_do_frame ()
@@ -3003,115 +2987,463 @@ dwarf2out_frame_debug_after_prologue (vo
 #endif
 	  )
 	dwarf2out_frame_debug (insn, true);
-    }
 
-  add_cfis_to_fde ();
+      if (JUMP_P (insn))
+	{
+	  rtx label = JUMP_LABEL (insn);
+	  if (label)
+	    {
+	      rtx next = next_real_insn (label);
+	      if (next != NULL_RTX && addr_vec_p (next))
+		{
+		  int i;
+		  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),
+					      start_points, uid_luid,
+					      point_info);
+		}
+	      else
+		maybe_record_jump_target (label, start_points, 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, start_points,
+				      uid_luid, point_info);
+	}
+    }
 }
 
-void
-dwarf2out_emit_cfi (dw_cfi_ref cfi)
+/* A subroutine of dwarf2out_debug_after_prologue.  Given the vector
+   of potential starting points in *START_POINTS, pick the best one to
+   use for the next scan.  Return NULL_RTX if there's nothing left to
+   scan.
+   UID_LUID and START_POINTS are as in scan_until_barrier.  */
+
+static rtx
+find_best_starting_point (jump_target_info *point_info, int *uid_luid,
+			  VEC (rtx, heap) **start_points)
 {
-  if (dwarf2out_do_cfi_asm ())
-    output_cfi_directive (cfi);
+  int i;
+  rtx insn;
+  int best_idx;
+  bool best_has_barrier;
+  jump_target_info *restart_info;
+
+  FOR_EACH_VEC_ELT_REVERSE (rtx, *start_points, i, insn)
+    {
+      restart_info = point_info + uid_luid[INSN_UID (insn)];
+      if (restart_info->visited)
+	VEC_ordered_remove (rtx, *start_points, i);
+    }
+
+  best_idx = -1;
+  best_has_barrier = false;
+  FOR_EACH_VEC_ELT (rtx, *start_points, i, insn)
+    {
+      rtx prev;
+      bool this_has_barrier;
+
+      restart_info = point_info + uid_luid[INSN_UID (insn)];
+      prev = prev_nonnote_nondebug_insn (insn);
+      this_has_barrier = (prev
+			  && (BARRIER_P (prev) || switch_note_p (prev)));
+      if (best_idx < 0
+	  || (!best_has_barrier && this_has_barrier))
+	{
+	  best_idx = i;
+	  best_has_barrier = this_has_barrier;
+	}
+    }
+
+  if (best_idx < 0)
+    {
+      rtx link;
+      for (link = forced_labels; link; link = XEXP (link, 1))
+	{
+	  insn = XEXP (link, 0);
+	  restart_info = point_info + uid_luid[INSN_UID (insn)];
+	  if (!restart_info->visited)
+	    return insn;
+	}
+      return NULL_RTX;
+    }
+  insn = VEC_index (rtx, *start_points, best_idx);
+  VEC_ordered_remove (rtx, *start_points, best_idx);
+  return insn;
 }
 
-/* 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.  */
+/* After the (optional) text prologue has been written, emit CFI insns
+   and update the FDE for frame-related instructions.  */
 
 void
-dwarf2out_cfi_begin_epilogue (rtx insn)
+dwarf2out_frame_debug_after_prologue (void)
 {
-  bool saw_frp = false;
-  rtx i;
+  int max_uid = get_max_uid ();
+  int i, n_saves_restores, prologue_end_point, switch_note_point;
+  rtx insn, save_point;
+  VEC (rtx, heap) *start_points;
+  int n_points;
+  int *uid_luid;
+  bool remember_needed;
+  jump_target_info *point_info, *save_point_info;
+  cfi_vec current_vec;
+
+  n_points = 0;
+  for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
+    if (save_point_p (insn))
+      n_points++;
+  uid_luid = XCNEWVEC (int, max_uid);
+  n_points = 0;
+  prologue_end_point = -1;
+  switch_note_point = -1;
+  for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
+    if (save_point_p (insn))
+      {
+	if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_PROLOGUE_END)
+	  prologue_end_point = n_points;
+	else if (switch_note_p (insn))
+	  switch_note_point = n_points;
+	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;
+
+  start_points = VEC_alloc (rtx, heap, 20);
+  insn = get_insns ();
+  current_vec = VEC_alloc (dw_cfi_ref, gc, 10);
+
+  /* At a NOTE_INSN_SWITCH_TEXT_SECTIONS we'll emit a cfi_startproc.
+     Ensure the state at this note reflects that.  */
+  if (switch_note_point != -1)
+    {
+      cfi_insn_vec = &current_vec;
+      record_current_state (point_info + switch_note_point);
+      cfi_insn_vec = NULL;
+    }
+  args_size = old_args_size = 0;
 
-  /* Scan forward to the return insn, noticing if there are possible
-     frame related insns.  */
-  for (i = NEXT_INSN (insn); i ; i = NEXT_INSN (i))
+  for (;;)
     {
-      if (!INSN_P (i))
-	continue;
+      HOST_WIDE_INT offset;
+      jump_target_info *restart_info;
 
-      /* 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))
+      /* Scan the insns and emit NOTE_CFIs where necessary.  */
+      cfi_insn_vec = &current_vec;
+      scan_until_barrier (insn, point_info, uid_luid, &start_points);
+      cfi_insn_vec = NULL;
+
+      insn = find_best_starting_point (point_info, uid_luid, &start_points);
+
+      if (insn == NULL_RTX)
 	break;
 
-      if (GET_CODE (PATTERN (i)) == SEQUENCE)
-	{
-	  int idx;
-	  rtx seq = PATTERN (i);
+      if (dump_file)
+	fprintf (dump_file, "restarting scan at label %d", INSN_UID (insn));
 
-	  if (returnjump_p (XVECEXP (seq, 0, 0)))
-	    break;
-	  if (CALL_P (XVECEXP (seq, 0, 0))
-	      && SIBLING_CALL_P (XVECEXP (seq, 0, 0)))
-	    break;
+      restart_info = point_info + uid_luid[INSN_UID (insn)];
+      restart_info->visited = true;
+      restart_info->used_as_start = true;
+      /* If find_best_starting_point returned a forced label, use the
+	 state at the NOTE_INSN_PROLOGUE_END note.  */
+      if (restart_info->cfis == NULL)
+	{
+	  cfi_vec *v = &restart_info->cfis;
+	  gcc_assert (prologue_end_point != -1);
+	  restart_info = point_info + prologue_end_point;
+	  *v = copy_cfi_vec_parts (restart_info->cfis);
+	}
+
+      gcc_assert (LABEL_P (insn));
+      current_vec = copy_cfi_vec_parts (restart_info->cfis);
+      cfa = restart_info->cfa;
+      old_cfa = restart_info->old_cfa;
+      cfa_store = restart_info->cfa_store;
+      offset = restart_info->args_size;
+      if (offset >= 0)
+	{
+	  if (dump_file && offset != args_size)
+	    fprintf (dump_file, ", args_size " HOST_WIDE_INT_PRINT_DEC
+		     "  -> " HOST_WIDE_INT_PRINT_DEC,
+		     args_size, offset);
 
-	  for (idx = 0; idx < XVECLEN (seq, 0); idx++)
-	    if (RTX_FRAME_RELATED_P (XVECEXP (seq, 0, idx)))
-	      saw_frp = true;
+	  offset -= args_size;
+#ifndef STACK_GROWS_DOWNWARD
+	  offset = -offset;
+#endif
+	  if (offset != 0)
+	    {
+	      cfi_insn = prev_nonnote_nondebug_insn (insn);
+	      dwarf2out_stack_adjust (offset);
+	      cfi_insn = NULL_RTX;
+	    }
+	}
+      if (dump_file)
+	{
+	  fprintf (dump_file, "\n");
+	  if (dump_flags & TDF_DETAILS)
+	    debug_cfi_vec (dump_file, current_vec);
 	}
 
-      if (RTX_FRAME_RELATED_P (i))
-	saw_frp = true;
+      insn = NEXT_INSN (insn);
     }
 
-  /* If the port doesn't emit epilogue unwind info, we don't need a
-     save/restore pair.  */
-  if (!saw_frp)
-    return;
+  VEC_free (rtx, heap, start_points);
 
-  /* Otherwise, search forward to see if the return insn was the last
-     basic block of the function.  If so, we don't need save/restore.  */
-  gcc_assert (i != NULL);
-  i = next_real_insn (i);
-  if (i == NULL)
-    return;
+  /* Now splice the various CFI fragments together into a coherent whole.  */
 
-  /* Insert the restore before that next real insn in the stream, and before
-     a potential NOTE_INSN_EPILOGUE_BEG -- we do need these notes to be
-     properly nested.  This should be after any label or alignment.  This
-     will be pushed into the CFI stream by the function below.  */
-  while (1)
+  /* First, discover discontinuities, and where necessary search for suitable
+     remember/restore points.  */
+  save_point = NULL_RTX;
+  save_point_info = NULL;
+  n_saves_restores = 0;
+  for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
     {
-      rtx p = PREV_INSN (i);
-      if (!NOTE_P (p))
-	break;
-      if (NOTE_KIND (p) == NOTE_INSN_BASIC_BLOCK)
-	break;
-      i = p;
+      jump_target_info *info, *barrier_info, *candidate_info;
+      rtx prev;
+
+      if (insn == save_point)
+	{
+	  save_point = NULL_RTX;
+	  save_point_info = NULL;
+	  info = point_info + uid_luid[INSN_UID (insn)];
+	  info->n_restores = n_saves_restores;
+	  n_saves_restores = 0;
+	  if (dump_file)
+	    fprintf (dump_file, "finalize save point %d\n", INSN_UID (insn));
+	}
+
+      /* Look for labels that were used as starting points and are
+	 preceded by a BARRIER.  */
+      if (!LABEL_P (insn))
+	continue;
+
+      info = point_info + uid_luid[INSN_UID (insn)];
+      if (!info->used_as_start)
+	continue;
+      barrier_info = NULL;
+      for (prev = PREV_INSN (insn); prev; prev = PREV_INSN (prev))
+	{
+	  if (!BARRIER_P (prev) && !LABEL_P (prev))
+	    continue;
+	  barrier_info = point_info + uid_luid[INSN_UID (prev)];
+	  /* Skip through barriers we haven't visited; they may occur
+	     for things like jump tables.  */
+	  if ((BARRIER_P (prev) && barrier_info->visited)
+	      || (LABEL_P (prev) && barrier_info->used_as_start)
+	      || switch_note_p (prev))
+	    break;
+	}
+      if (!BARRIER_P (prev))
+	continue;
+
+      if (dump_file)
+	fprintf (dump_file, "State transition at barrier %d, label %d ... ",
+		 INSN_UID (prev), INSN_UID (insn));
+
+      /* If the state at the barrier can easily be transformed into the state
+	 at the label, we don't need save/restore points.  */
+      if (vec_is_prefix_of (barrier_info->cfis, info->cfis))
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "prefix\n");
+	  continue;
+	}
+
+      /* A save/restore is necessary.  Walk backwards to find the best
+	 save point.  First see if we know a save point already and if
+	 it's suitable.  */
+      n_saves_restores++;
+      if (save_point)
+	{
+	  prev = save_point;
+	  if (vec_is_prefix_of (save_point_info->cfis, info->cfis))
+	    {
+	      if (dump_file)
+		fprintf (dump_file, "reuse save point\n");
+	      continue;
+	    }
+	}
+
+      for (;;)
+	{
+	  prev = PREV_INSN (prev);
+	  /* We should eventually encounter the NOTE_INSN_FUNCTION_BEG,
+	     which must be a suitable save point fo anything.  */
+	  gcc_assert (prev != NULL_RTX);
+
+	  if (!save_point_p (prev))
+	    continue;
+
+	  candidate_info = point_info + uid_luid[INSN_UID (prev)];
+	  /* We don't necessarily get to see this note during
+	     scanning. Record an empty CFI vector for it so that it is
+	     usable as a restore point.  */
+	  if (switch_note_p (prev))
+	    {
+	      if (candidate_info->cfis == NULL)
+		candidate_info->cfis = VEC_alloc (dw_cfi_ref, gc, 1);
+	    }
+
+	  if (candidate_info->cfis != NULL
+	      && vec_is_prefix_of (candidate_info->cfis, info->cfis)
+	      && (save_point == NULL
+		  || vec_is_prefix_of (candidate_info->cfis,
+				       save_point_info->cfis)))
+	    {
+	      if (dump_file)
+		fprintf (dump_file, "save point %d\n", INSN_UID (prev));
+	      save_point = prev;
+	      save_point_info = candidate_info;
+	      break;
+	    }
+	}
     }
-  emit_note_before (NOTE_INSN_CFA_RESTORE_STATE, i);
 
-  emit_cfa_remember = true;
+  save_point = NULL_RTX;
+  save_point_info = NULL;
+  remember_needed = false;
+
+  /* This value is now used to distinguish between NOTE_CFI added up
+     to now and those added by the next loop.  */
+  max_uid = get_max_uid ();
 
-  /* And emulate the state save.  */
-  gcc_assert (!cfa_remember.in_use);
-  cfa_remember = cfa;
-  old_cfa_remember = old_cfa;
-  cfa_remember.in_use = 1;
-}
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      jump_target_info *info;
 
-/* A "subroutine" of dwarf2out_cfi_begin_epilogue.  Emit the restore
-   required.  */
+      if (INSN_UID (insn) < max_uid
+	  && NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_CFI
+	  && remember_needed)
+	{
+	  cfi_insn = PREV_INSN (insn);
+	  add_cfa_remember ();
+	  cfi_insn = NULL_RTX;
+	  remember_needed = false;
+	}
 
-static void
-dwarf2out_frame_debug_restore_state (void)
-{
-  dw_cfi_ref cfi = new_cfi ();
+      if (!save_point_p (insn))
+	continue;
 
-  cfi->dw_cfi_opc = DW_CFA_restore_state;
-  add_fde_cfi (cfi);
+      cfi_insn = insn;
+      info = point_info + uid_luid[INSN_UID (insn)];
+
+      if (info->n_restores > 0)
+	{
+	  gcc_assert (save_point_info == NULL);
+	  save_point_info = info;
+	  remember_needed = true;
+	}
+      if (switch_note_p (insn))
+	{
+	  jump_target_info *label_info;
+	  rtx next = insn;
+
+	  cfi_insn = insn;
+	  if (remember_needed)
+	    add_cfa_remember ();
+	  remember_needed = false;
+
+	  /* Find the next label, and emit extra CFIs as necessary to
+	     achieve the correct state.  */
+	  do
+	    {
+	      if (LABEL_P (next))
+		{
+		  label_info = point_info + uid_luid[INSN_UID (next)];
+		  if (label_info->used_as_start)
+		    break;
+		}
+	      insn = next;
+	      next = NEXT_INSN (next);
+	    }
+	  while (next != NULL_RTX);
+	  if (next == NULL_RTX)
+	    break;
+	  append_extra_cfis (NULL, label_info->cfis);
+	  cfi_insn = NULL_RTX;
+	}
+      else if (BARRIER_P (insn))
+	{
+	  jump_target_info *label_info;
+	  cfi_vec new_cfi_vec;
+	  cfi_vec barrier_cfi = info->cfis;
+	  rtx next = insn;
+
+	  /* Find the start of the next sequence we processed.  */
+	  do
+	    {
+	      if (LABEL_P (next))
+		{
+		  label_info = point_info + uid_luid[INSN_UID (next)];
+		  if (label_info->used_as_start)
+		    break;
+		}
+	      if (switch_note_p (next))
+		break;
+	      insn = next;
+	      next = NEXT_INSN (next);
+	    }
+	  while (next != NULL_RTX);
+	  if (next == NULL_RTX)
+	    break;
+	  if (!LABEL_P (next))
+	    continue;
 
-  gcc_assert (cfa_remember.in_use);
-  cfa = cfa_remember;
-  old_cfa = old_cfa_remember;
-  cfa_remember.in_use = 0;
+	  /* Emit extra CFIs as necessary to achieve the correct state.  */
+	  new_cfi_vec = label_info->cfis;
+	  cfi_insn = next;
+	  if (vec_is_prefix_of (barrier_cfi, new_cfi_vec))
+	    {
+	      if (VEC_length (dw_cfi_ref, barrier_cfi)
+		  != VEC_length (dw_cfi_ref, new_cfi_vec))
+		{
+		  /* If the barrier was a point needing a restore, we must
+		     add the remember here as we ignore the newly added
+		     CFI notes.  */
+		  if (info->n_restores > 0)
+		    add_cfa_remember ();
+		  remember_needed = false;
+		  append_extra_cfis (barrier_cfi, new_cfi_vec);
+		}
+	    }
+	  else
+	    {
+	      save_point_info->n_restores--;
+	      dwarf2out_frame_debug_restore_state ();
+
+	      if (save_point_info->n_restores > 0)
+		add_cfa_remember ();
+	      gcc_assert (!remember_needed);
+	      append_extra_cfis (save_point_info->cfis, new_cfi_vec);
+	      if (save_point_info->n_restores == 0)
+		save_point_info = NULL;
+	    }
+	  cfi_insn = NULL_RTX;
+	}
+    }
+  free (uid_luid);
+  free (point_info);
+
+  add_cfis_to_fde ();
+}
+
+void
+dwarf2out_emit_cfi (dw_cfi_ref cfi)
+{
+  if (dwarf2out_do_cfi_asm ())
+    output_cfi_directive (asm_out_file, cfi);
 }
 
 /* Describe for the GTY machinery what parts of dw_cfi_oprnd1 are used.  */
@@ -3411,7 +3743,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;
 
@@ -3426,82 +3758,96 @@ output_cfi_directive (dw_cfi_ref cfi)
       /* Should only be created by add_fde_cfi in a code path not
 	 followed when emitting via directives.  The assembler is
 	 going to take care of this for us.  */
-      gcc_unreachable ();
+      if (f == asm_out_file)
+	gcc_unreachable ();
+      fprintf (f, "\t.cfi_advance_loc\n");
+      break;
 
     case DW_CFA_offset:
     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);
+      if (f != asm_out_file)
+	{
+	  fprintf (f, "\t.cfi_GNU_args_size"HOST_WIDE_INT_PRINT_DEC "\n",
+		   cfi->dw_cfi_oprnd1.dw_cfi_offset);
+	  break;
+	}
+      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);
+      if (f != asm_out_file)
+	{
+	  fprintf (f, "\t.cfi_cfa_{def_,}expression\n");
+	  break;
+	}
+      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:
@@ -3510,14 +3856,11 @@ output_cfi_directive (dw_cfi_ref 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.  */
+   same state as after executing CFIs in CFI chain.  FDE and FOR_EH
+   are the other arguments to pass to output_cfi.  */
 
 static void
-output_cfis (cfi_vec vec, int upto, bool do_cfi_asm,
-	     dw_fde_ref fde, bool for_eh)
+output_cfis (cfi_vec vec, int upto, dw_fde_ref fde, bool for_eh)
 {
   int ix;
   struct dw_cfi_struct cfi_buf;
@@ -3611,12 +3954,7 @@ output_cfis (cfi_vec vec, int upto, bool
 	      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);
-		}
+		output_cfi (cfi2, fde, for_eh);
 	    }
 	  if (cfi_cfa && cfi_cfa_offset && cfi_cfa_offset != cfi_cfa)
 	    {
@@ -3645,30 +3983,20 @@ output_cfis (cfi_vec vec, int upto, bool
 	  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);
-	    }
+	    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);
-	    }
+	    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;
@@ -3678,14 +4006,6 @@ output_cfis (cfi_vec vec, int upto, bool
     }
 }
 
-/* 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
@@ -3801,7 +4121,7 @@ output_fde (dw_fde_ref fde, bool for_eh,
       if (fde->dw_fde_switch_cfi_index > 0)
 	{
 	  from = fde->dw_fde_switch_cfi_index;
-	  output_cfis (fde->dw_fde_cfi, from, false, fde, for_eh);
+	  output_cfis (fde->dw_fde_cfi, from, fde, for_eh);
 	}
       for (i = from; i < until; i++)
 	output_cfi (VEC_index (dw_cfi_ref, fde->dw_fde_cfi, i),
@@ -4379,13 +4699,8 @@ dwarf2out_switch_text_section (void)
        || (cold_text_section && sect == cold_text_section));
 
   if (dwarf2out_do_cfi_asm ())
-    {
-      dwarf2out_do_cfi_startproc (true);
-      /* As this is a different FDE, insert all current CFI instructions
-	 again.  */
-      output_all_cfis (fde->dw_fde_cfi, true, fde, true);
-    }
-  fde->dw_fde_switch_cfi_index = VEC_length (dw_cfi_ref, fde->dw_fde_cfi);
+    dwarf2out_do_cfi_startproc (true);
+
   var_location_switch_text_section ();
 
   set_cur_line_info_table (sect);
@@ -5490,7 +5805,7 @@ output_loc_operands_raw (dw_loc_descr_re
 	dw2_asm_output_data_uleb128_raw (r);
       }
       break;
-      
+
     case DW_OP_constu:
     case DW_OP_plus_uconst:
     case DW_OP_piece:
@@ -12472,7 +12787,7 @@ output_one_line_info_table (dw_line_info
 	  dw2_asm_output_data (1, DW_LNS_set_prologue_end,
 			       "set prologue end");
 	  break;
-	  
+
 	case LI_set_epilogue_begin:
 	  dw2_asm_output_data (1, DW_LNS_set_epilogue_begin,
 			       "set epilogue begin");
@@ -14799,7 +15114,7 @@ static bool
 decl_by_reference_p (tree decl)
 {
   return ((TREE_CODE (decl) == PARM_DECL || TREE_CODE (decl) == RESULT_DECL
-  	   || TREE_CODE (decl) == VAR_DECL)
+	   || TREE_CODE (decl) == VAR_DECL)
 	  && DECL_BY_REFERENCE (decl));
 }
 
@@ -20724,7 +21039,7 @@ gen_type_die_with_usage (tree type, dw_d
       if (DECL_CONTEXT (TYPE_NAME (type))
 	  && TREE_CODE (DECL_CONTEXT (TYPE_NAME (type))) == NAMESPACE_DECL)
 	context_die = get_context_die (DECL_CONTEXT (TYPE_NAME (type)));
-      
+
       gen_decl_die (TYPE_NAME (type), NULL, context_die);
       return;
     }
@@ -21954,7 +22269,7 @@ gen_scheduled_generic_parms_dies (void)
 
   if (generic_type_instances == NULL)
     return;
-  
+
   FOR_EACH_VEC_ELT (tree, generic_type_instances, i, t)
     gen_generic_params_dies (t);
 }
@@ -23863,7 +24178,7 @@ dwarf2out_finish (const char *filename)
   if (!VEC_empty (pubname_entry, pubtype_table))
     {
       bool empty = false;
-      
+
       if (flag_eliminate_unused_debug_types)
 	{
 	  /* The pubtypes table might be emptied by pruning unused items.  */
Index: gcc/jump.c
===================================================================
--- gcc.orig/jump.c
+++ gcc/jump.c
@@ -709,6 +709,15 @@ comparison_dominates_p (enum rtx_code co
   return 0;
 }
 
+/* Return true if INSN is an ADDR_VEC or ADDR_DIFF_VEC.  */
+bool
+addr_vec_p (const_rtx insn)
+{
+  return (JUMP_P (insn)
+	  && (GET_CODE (PATTERN (insn)) == ADDR_VEC
+	      || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC));
+}
+
 /* Return 1 if INSN is an unconditional jump and nothing else.  */
 
 int
Index: gcc/rtl.h
===================================================================
--- gcc.orig/rtl.h
+++ gcc/rtl.h
@@ -2307,6 +2307,7 @@ extern int any_condjump_p (const_rtx);
 extern int any_uncondjump_p (const_rtx);
 extern rtx pc_set (const_rtx);
 extern rtx condjump_label (const_rtx);
+extern bool addr_vec_p (const_rtx);
 extern int simplejump_p (const_rtx);
 extern int returnjump_p (rtx);
 extern int eh_returnjump_p (rtx);

Patch

Index: gcc/dwarf2out.c
===================================================================
--- gcc.orig/dwarf2out.c
+++ gcc/dwarf2out.c
@@ -470,6 +470,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);
@@ -847,6 +849,15 @@  add_cfi (cfi_vec *vec, dw_cfi_ref cfi)
   VEC_safe_push (dw_cfi_ref, gc, *vec, cfi);
 }
 
+/* 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;
+
+/* True if any CFI directives were emitted at the current insn.  */
+static bool any_cfis_emitted;
+
 /* Generate a new label for the CFI info to refer to.  FORCE is true
    if a label needs to be output even when using .cfi_* directives.  */
 
@@ -866,18 +877,13 @@  dwarf2out_cfi_label (bool force)
     {
       int num = dwarf2out_cfi_label_num++;
       ASM_GENERATE_INTERNAL_LABEL (label, "LCFI", num);
-      ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LCFI", num);
+      cfi_insn = emit_note_after (NOTE_INSN_CFI_LABEL, cfi_insn);
+      NOTE_LABEL_NUMBER (cfi_insn) = num;
     }
 
   return label;
 }
 
-/* True if remember_state should be emitted before following CFI directive.  */
-static bool emit_cfa_remember;
-
-/* True if any CFI directives were emitted at the current insn.  */
-static bool any_cfis_emitted;
-
 /* Add CFI to the current fde at the PC value indicated by LABEL if specified,
    or to the CIE if LABEL is NULL.  */
 
@@ -957,7 +963,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;
 
 	  vec = &fde->dw_fde_cfi;
 	  any_cfis_emitted = true;
@@ -2791,6 +2798,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 ();
 
@@ -2914,6 +2926,7 @@  void
 dwarf2out_frame_debug_init (void)
 {
   size_t i;
+  rtx insn;
 
   /* Flush any queued register saves.  */
   dwarf2out_flush_queued_reg_saves ();
@@ -2940,12 +2953,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)
@@ -2960,8 +3025,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,12 @@  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)
+
+/* When emitting dwarf2 frame information, contains the number of a debug
+   label that should be emitted.  */
+INSN_NOTE (CFI_LABEL)
+
 #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,8 @@  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_LABEL_NUMBER(INSN)	XCINT (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/final.c
===================================================================
--- gcc.orig/final.c
+++ gcc/final.c
@@ -1678,7 +1678,7 @@  final_end_function (void)
 void
 final (rtx first, FILE *file, int optimize_p)
 {
-  rtx insn;
+  rtx insn, next;
   int max_uid = 0;
   int seen = 0;
 
@@ -1723,6 +1723,15 @@  final (rtx first, FILE *file, int optimi
 
       insn = final_scan_insn (insn, file, optimize_p, 0, &seen);
     }
+
+  for (insn = first; insn; insn = next)
+    {
+      next = NEXT_INSN (insn);
+      if (NOTE_P (insn)
+	  && (NOTE_KIND (insn) == NOTE_INSN_CFI
+	      || NOTE_KIND (insn) == NOTE_INSN_CFI_LABEL))
+	delete_insn (insn);
+    }
 }
 
 const char *
@@ -1899,16 +1908,19 @@  final_scan_insn (rtx insn, FILE *file, i
 	  break;
 
 	case NOTE_INSN_EPILOGUE_BEG:
-#if defined (HAVE_epilogue)
-	  if (dwarf2out_do_frame ())
-	    dwarf2out_cfi_begin_epilogue (insn);
-#endif
 	  (*debug_hooks->begin_epilogue) (last_linenum, last_filename);
 	  targetm.asm_out.function_begin_epilogue (file);
 	  break;
 
 	case NOTE_INSN_CFA_RESTORE_STATE:
-	  dwarf2out_frame_debug_restore_state ();
+	  break;
+
+	case NOTE_INSN_CFI:
+	  dwarf2out_emit_cfi (NOTE_CFI (insn));
+	  break;
+
+	case NOTE_INSN_CFI_LABEL:
+	  ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LCFI", NOTE_LABEL_NUMBER (insn));
 	  break;
 
 	case NOTE_INSN_FUNCTION_BEG:
@@ -2018,8 +2030,6 @@  final_scan_insn (rtx insn, FILE *file, i
       break;
 
     case BARRIER:
-      if (dwarf2out_do_frame ())
-	dwarf2out_frame_debug (insn, false);
       break;
 
     case CODE_LABEL:
@@ -2285,12 +2295,6 @@  final_scan_insn (rtx insn, FILE *file, i
 
 	    final_sequence = body;
 
-	    /* Record the delay slots' frame information before the branch.
-	       This is needed for delayed calls: see execute_cfa_program().  */
-	    if (dwarf2out_do_frame ())
-	      for (i = 1; i < XVECLEN (body, 0); i++)
-		dwarf2out_frame_debug (XVECEXP (body, 0, i), false);
-
 	    /* The first insn in this SEQUENCE might be a JUMP_INSN that will
 	       force the restoration of a comparison that was previously
 	       thought unnecessary.  If that happens, cancel this sequence
@@ -2604,9 +2608,6 @@  final_scan_insn (rtx insn, FILE *file, i
 
 	current_output_insn = debug_insn = insn;
 
-	if (CALL_P (insn) && dwarf2out_do_frame ())
-	  dwarf2out_frame_debug (insn, false);
-
 	/* Find the proper template for this insn.  */
 	templ = get_insn_template (insn_code_number, insn);
 
@@ -2686,16 +2687,6 @@  final_scan_insn (rtx insn, FILE *file, i
 	  targetm.asm_out.final_postscan_insn (file, insn, recog_data.operand,
 					       recog_data.n_operands);
 
-	/* If necessary, report the effect that the instruction has on
-	   the unwind info.   We've already done this for delay slots
-	   and call instructions.  */
-	if (final_sequence == 0
-#if !defined (HAVE_prologue)
-	    && !ACCUMULATE_OUTGOING_ARGS
-#endif
-	    && dwarf2out_do_frame ())
-	  dwarf2out_frame_debug (insn, true);
-
 	if (!targetm.asm_out.unwind_emit_before_insn
 	    && targetm.asm_out.unwind_emit)
 	  targetm.asm_out.unwind_emit (asm_out_file, insn);