public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [RFA] Compact EH  Patch
@ 2015-07-30 21:07 Moore, Catherine
  2015-08-14 19:58 ` Moore, Catherine
                   ` (3 more replies)
  0 siblings, 4 replies; 14+ messages in thread
From: Moore, Catherine @ 2015-07-30 21:07 UTC (permalink / raw)
  To: gcc-patches; +Cc: jason, Matthew Fortune

[-- Attachment #1: Type: text/plain, Size: 844 bytes --]

This patch implements a more compact format for exception handling data.  Although I don't have recent numbers for the amount of compression achieved, an earlier measurement showed a 30% reduction in the size of EH data for libstdc++. 

A design document detailing the new format is available (https://github.com/MentorEmbedded/cxx-abi/blob/master/MIPSCompactEH.pdf).

This implementation enables the new format for MIPS targets only, but the generic pieces to enable the new format for other architectures is in place.
I couldn't really think of a good way to split up the patch to make the review easier, but I am open to suggestions.
I've successfully bootstrapped with the patch and completed testing across many of the MIPS targets and multilibs.
I also ran a test series for the x86 without regressions.
Thanks,
Catherine




[-- Attachment #2: compact-eh.cl --]
[-- Type: application/octet-stream, Size: 4926 bytes --]

2015-07-30  Catherine Moore  <clm@codesourcery.com>
            Bernd Schmidt    <bernds@codesourcery.com>
            Paul Brook  <paul@codesourcery.com>

	gcc/
	* config/mips/sde.h (MIPS_COMPACT_EH_PCREL): Define.
	* config/mips/mips-protos.h (mips_fixup_cfi_sections): Declare.
	* config/mips/linux.h (MD_HAVE_COMPACT_EH): Define.
	* config/mips/elf.h (MD_HAVE_COMPACT_EH): Define.
	* config/mips/mips.opt (mcompact-eh): New option.
	* config/mips/mips.c: Include except.h.
	(done_cfi_sections): New.
	(mips_fixup_cfi_sections): New.
	(mips16_build_call_stub): Emit Compact EH cfi data.
	(mips_asm_init_sections): New.
	(mips_in_small_data_p): Add Compact EH support.
	(mips_file_start): Likewise.
	(add_fp_ra_push): New.
	(mips_fdedata_cfaadjust): New.
	(mips_cfi_endproc): New.
	(TARGET_ASM_INIT_SECTIONS): New.
	(TARGET_OUTPUT_CFI_ENDPROC): Define.
	* config/mips/mips.h (TARGET_CPU_CPP_BUILTINS): Add __GNU_COMPACT_EH__.
	(ASM_DECLARE_FUNCTION_NAME): Define.
	(MIPS_COMPACT_EH_PCREL): Define.
	(MIPS_COMPACT_EH_ENCODING): Define.
	(ASM_PREFERRED_EH_DATA): Redefine.
	(ASM_MAYBE_OUTPUT_ENCODED_ADDR_RTX): Define.
	(MIPS_EH_ENCODING): Define.
	(md_unwind_compact_opcode_finish): Define.
	(CRT_GET_RFIB_DATA): Define.
	* doc/tm.texi: Regenerate.
	* doc/tm.texi.in (TARGET_OUTPUT_CFI_ENDPROC): New hook.
	* doc/invoke.texi (mcompact-eh): Document.
	* dwarf2out.c (FUNC_BEGIN_SWITCH_LABEL): Define.
	(output_call_frame_info): Add support for Compact EH.
	(dwarf2out_do_cfi_startproc):  Likewise.
	(dwarf2out_end_epilogue): Likewise.
	(dwarf2out_switch_text_section): Likewise.
	* except.c (using_compact_pr): New.
	(compact_pr_id): New.
	(read_sleb128): New.
	(push_uleb128): Make external.
	(func_begin_lab, switch_begin_lab): New.
	(region_begin_lab, region_end_lab): New.
	(chain_d, chain_ref, chains): Declare.
	(compact_info_d, compact_info_ref, compact_info): Declare.
	(dummy_info_d, dummy_info_ref, dummy_info): Declare.
	(compact_create_region_start_label): New.
	(compact_create_region_end_label): New.
	(compact_emit_region_header): New.
	(compact_insert_nothrow): New.
	(compact_insert_continue): New.
	(compact_insert_cleanup): New.
	(compact_link_to_dummy): New.
	(compact_emit_dummies): New.
	* targhooks.c (default_asm_output_cfi_endproc): New.
	* targhooks.h (default_asm_output_cfi_endproc): Declare.

	libgcc/
	* config.host (mips*-sde-elf*): Set md_unwind_header.
	* Makefile.in (LIB2ADDEH): Add unwind-compact.c.
	* unwind-dw2-fde-dip.c: Include unwind-compact.h.
	(_Unwind_Find_registered_Index): Add declaration.
	(unw_eh_callback_data): Use struct compact_eh_bases.  Add type.
	(base_from_cb_data): Use data->bases.
	(_Unwind_IteratePhdrCallback): Search compact frame headers.
	Use data->bases.  Set data->type.
	(_Unwind_Find_FDE): Rename...
	(_Unwind_Find_Index): To this.  Adjust for compact EH.
	* unwind-compact.c: New file.
	* unwind-compact.h: New file.
	* unwind-dw2-fde.c: Include unwind-compact.h.
	(__register_frame_info_1, __register_frame_info_header_bases): New
	functions.
	(__register_frame_info_bases): Use __register_frame_info_1.
	(search_object): Search compact unwind table headers.
	Set bases->entry.  Return entry type.
	(_Unwind_Find_FDE): Implement as wrapper around _Unwind_Find_Index.
	(_Unwind_Find_registered_Index): New function.
	* unwind-dw2-fde.h (object): Add flag for compact frame headers.
	(__register_frame_info_header_bases): Add prototype.
	(compact_eh_bases, compact_entry_type): New.
	(_Unwind_Find_Index): Add prototype.
	* crtstuff.c (__register_frame_info_header_bases): Declare
	(__GNU_EH_FRAME_HDR): Declare.
	(frame_dummy): Register eh_frame_hdr tables.
	* config/t-eh-dw2-dip (LIB2ADDEH): Add $(srcdir)/unwind-compact.c.
	* config/mips/linux-unwind.h: Include mips-unwind.h.
	* config/mips/mips-unwind.h: New file.
	* unwind-dw2.c (__gnu_compact_pr1, uw_frame_state_compact):
	New functions.
	(__gnu_compact_pr2): Use weak references.
	(uw_frame_state_for): Handle compact frame entries.
	* mips16.S: Add Compact EH support.
	* unwind-compat.c (_Unwind_GetEhEncoding): New.
	* unwind-pe.h (DW_EH_PE_udata1): Define.
	(size_of_encoded_value, read_encoded_value_with_base): Handle
	DW_EH_PE_udata1.
	(unaligned): Add u1.
	* unwind-generic.h (_Unwind_GetEhEncoding): Declare.
	* libgcc-std.ver.in: Add _Unwind_GetEhEncoding and

	libstdc++-v3/
	* libsupc++/eh_personality.cc (compact_get_ttype_entry): New.
	(check_compact_exception_spec): New.
	(__cxa_call_unexpected): Handle Compact EH.
	* libsupc++/Makefile.am (eh_compact_pr.cc): Add to sources.
	* libsupc++/Makefile.in: Regenerate.
	* libsupc++/eh_compact_pr.cc: New file.
	* libsupc++/unwind-cxx.h (__gnu_compact_pr2): Declare.
	* config/abi/pre/gnu.ver (__gnu_compact_pr2): Add to CXXABI_1.3.
	* config/abi/pre/gnu-versioned-namespace.ver (__gnu_compact_pr2):
	Add to CXXABI_2.0.

[-- Attachment #3: compact-eh.patch --]
[-- Type: application/octet-stream, Size: 130210 bytes --]

Index: libgcc/libgcc-std.ver.in
===================================================================
--- libgcc/libgcc-std.ver.in	(revision 226409)
+++ libgcc/libgcc-std.ver.in	(working copy)
@@ -1918,6 +1918,7 @@ GCC_4.6.0 {
   __morestack_current_segment
   __morestack_initial_sp
   __splitstack_find
+  _Unwind_GetEhEncoding
 }
 
 %inherit GCC_4.7.0 GCC_4.6.0
@@ -1938,3 +1939,8 @@ GCC_4.7.0 {
 %inherit GCC_4.8.0 GCC_4.7.0
 GCC_4.8.0 {
 }
+
+%inherit GCC_4.8.0 GCC_4.7.0
+GCC_4.8.0 {
+  __register_frame_info_header_bases
+}
Index: libgcc/config.host
===================================================================
--- libgcc/config.host	(revision 226409)
+++ libgcc/config.host	(working copy)
@@ -854,6 +854,7 @@ mips*-*-linux*)				# Linux MIPS, either endian.
 	;;
 mips*-sde-elf*)
 	tmake_file="$tmake_file mips/t-crtstuff mips/t-mips16"
+	md_unwind_header=mips/mips-unwind.h
 	case "${with_newlib}" in
 	  yes)
 	    # newlib / libgloss.
Index: libgcc/Makefile.in
===================================================================
--- libgcc/Makefile.in	(revision 226409)
+++ libgcc/Makefile.in	(working copy)
@@ -330,7 +330,7 @@ TPBIT_FUNCS = _pack_tf _unpack_tf _addsub_tf _mul_
 
 # Additional sources to handle exceptions; overridden by targets as needed.
 LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde.c \
-  $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c
+  $(srcdir)/unwind-compact.c $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c
 LIB2ADDEHSTATIC = $(LIB2ADDEH)
 LIB2ADDEHSHARED = $(LIB2ADDEH)
 
Index: libgcc/unwind-dw2-fde-dip.c
===================================================================
--- libgcc/unwind-dw2-fde-dip.c	(revision 226409)
+++ libgcc/unwind-dw2-fde-dip.c	(working copy)
@@ -45,6 +45,9 @@
 #include "unwind-dw2-fde.h"
 #include "unwind-compat.h"
 #include "gthr.h"
+#ifdef MD_HAVE_COMPACT_EH
+#include "unwind-compact.h"
+#endif
 
 #if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
     && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
@@ -90,11 +93,11 @@
 # define __RELOC_POINTER(ptr, base) ((ptr) + (base))
 #endif
 
-static const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases);
+static enum compact_entry_type
+_Unwind_Find_registered_Index (void *pc, struct compact_eh_bases *bases);
 
-#define _Unwind_Find_FDE _Unwind_Find_registered_FDE
+#define _Unwind_Find_registered_Index _Unwind_Find_registered_Index
 #include "unwind-dw2-fde.c"
-#undef _Unwind_Find_FDE
 
 #ifndef PT_GNU_EH_FRAME
 #define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550)
@@ -103,10 +106,8 @@
 struct unw_eh_callback_data
 {
   _Unwind_Ptr pc;
-  void *tbase;
-  void *dbase;
-  void *func;
-  const fde *ret;
+  struct compact_eh_bases *bases;
+  enum compact_entry_type type;
   int check_cache;
 };
 
@@ -149,9 +150,9 @@ base_from_cb_data (unsigned char encoding, struct
       return 0;
 
     case DW_EH_PE_textrel:
-      return (_Unwind_Ptr) data->tbase;
+      return (_Unwind_Ptr) data->bases->tbase;
     case DW_EH_PE_datarel:
-      return (_Unwind_Ptr) data->dbase;
+      return (_Unwind_Ptr) data->bases->dbase;
     default:
       gcc_unreachable ();
     }
@@ -173,6 +174,7 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *
   _Unwind_Ptr eh_frame;
   struct object ob;
   _Unwind_Ptr pc_low = 0, pc_high = 0;
+  const fde *f;
 
   struct ext_dl_phdr_info
     {
@@ -321,12 +323,11 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *
   /* Read .eh_frame_hdr header.  */
   hdr = (const struct unw_eh_frame_hdr *)
     __RELOC_POINTER (p_eh_frame_hdr->p_vaddr, load_base);
-  if (hdr->version != 1)
-    return 1;
 
 #ifdef CRT_GET_RFIB_DATA
-# ifdef __i386__
-  data->dbase = NULL;
+# if defined(__i386__) || defined(__mips__)
+  data->bases->dbase = NULL;
+
   if (p_dynamic)
     {
       /* For dynamically linked executables and shared libraries,
@@ -336,8 +337,11 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *
       for (; dyn->d_tag != DT_NULL ; dyn++)
 	if (dyn->d_tag == DT_PLTGOT)
 	  {
-	    data->dbase = (void *) dyn->d_un.d_ptr;
-#if defined __linux__
+	    data->bases->dbase = (void *) dyn->d_un.d_ptr;
+#if defined(__mips__)
+	    /* MIPS targets need to relocated the value and the offset.  */
+	    data->bases->dbase += load_base + 0x7ff0;
+#elif defined __linux__
 	    /* On IA-32 Linux, _DYNAMIC is writable and GLIBC has
 	       relocated it.  */
 #elif defined __sun__ && defined __svr4__
@@ -354,6 +358,20 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *
 # endif
 #endif
 
+#ifdef MD_HAVE_COMPACT_EH
+  if (hdr->version == 2)
+    {
+      data->type =
+	_Unwind_Search_Compact_Eh_Hdr ((void *)data->pc,
+				       (const unsigned char *) hdr,
+				       data->bases);
+      return 1;
+    }
+#endif
+
+  if (hdr->version != 1)
+    return 1;
+
   p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,
 				    base_from_cb_data (hdr->eh_frame_ptr_enc,
 						       data),
@@ -384,7 +402,6 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *
 	  const struct fde_table *table = (const struct fde_table *) p;
 	  size_t lo, hi, mid;
 	  _Unwind_Ptr data_base = (_Unwind_Ptr) hdr;
-	  fde *f;
 	  unsigned int f_enc, f_enc_size;
 	  _Unwind_Ptr range;
 
@@ -416,8 +433,11 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *
 	  read_encoded_value_with_base (f_enc & 0x0f, 0,
 					&f->pc_begin[f_enc_size], &range);
 	  if (data->pc < table[mid].initial_loc + data_base + range)
-	    data->ret = f;
-	  data->func = (void *) (table[mid].initial_loc + data_base);
+	    {
+	      data->bases->entry = f;
+	      data->type = CET_FDE;
+	    }
+	  data->bases->func = (void *) (table[mid].initial_loc + data_base);
 	  return 1;
 	}
     }
@@ -426,58 +446,55 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *
      As soon as GLIBC will provide API so to notify that a library has been
      removed, we could cache this (and thus use search_object).  */
   ob.pc_begin = NULL;
-  ob.tbase = data->tbase;
-  ob.dbase = data->dbase;
+  ob.tbase = data->bases->tbase;
+  ob.dbase = data->bases->dbase;
   ob.u.single = (fde *) eh_frame;
   ob.s.i = 0;
   ob.s.b.mixed_encoding = 1;  /* Need to assume worst case.  */
-  data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc);
-  if (data->ret != NULL)
+  f = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc);
+  data->bases->entry = f;
+  if (f != NULL)
     {
       _Unwind_Ptr func;
-      unsigned int encoding = get_fde_encoding (data->ret);
+      unsigned int encoding = get_fde_encoding (f);
 
       read_encoded_value_with_base (encoding,
 				    base_from_cb_data (encoding, data),
-				    data->ret->pc_begin, &func);
-      data->func = (void *) func;
+				    f->pc_begin, &func);
+      data->bases->func = (void *) func;
+      data->type = CET_FDE;
     }
   return 1;
 }
 
-const fde *
-_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
+enum compact_entry_type
+_Unwind_Find_Index (void *pc, struct compact_eh_bases *bases)
 {
   struct unw_eh_callback_data data;
-  const fde *ret;
 
-  ret = _Unwind_Find_registered_FDE (pc, bases);
-  if (ret != NULL)
-    return ret;
+  data.type = _Unwind_Find_registered_Index (pc, bases);
+  if (data.type != CET_not_found)
+    return data.type;
 
   data.pc = (_Unwind_Ptr) pc;
-  data.tbase = NULL;
-  data.dbase = NULL;
-  data.func = NULL;
-  data.ret = NULL;
+  data.bases = bases;
+  data.type = CET_not_found;
   data.check_cache = 1;
 
   if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
-    return NULL;
+    return CET_not_found;
 
-  if (data.ret)
-    {
-      bases->tbase = data.tbase;
-      bases->dbase = data.dbase;
-      bases->func = data.func;
-    }
-  return data.ret;
+  if (data.type != CET_not_found)
+    return data.type;
+
+  return CET_not_found;
 }
 
-#else
-/* Prevent multiple include of header files.  */
-#define _Unwind_Find_FDE _Unwind_Find_FDE
+#else /* !USE_PT_GNU_EH_FRAME */
+
+#define _Unwind_Find_registered_Index _Unwind_Find_Index
 #include "unwind-dw2-fde.c"
+
 #endif
 
 #if defined (USE_GAS_SYMVER) && defined (SHARED) && defined (USE_LIBUNWIND_EXCEPTIONS)
Index: libgcc/unwind-pe.h
===================================================================
--- libgcc/unwind-pe.h	(revision 226409)
+++ libgcc/unwind-pe.h	(working copy)
@@ -44,6 +44,7 @@
 #define DW_EH_PE_udata2         0x02
 #define DW_EH_PE_udata4         0x03
 #define DW_EH_PE_udata8         0x04
+#define DW_EH_PE_udata1         0x05
 #define DW_EH_PE_sleb128        0x09
 #define DW_EH_PE_sdata2         0x0A
 #define DW_EH_PE_sdata4         0x0B
@@ -78,6 +79,8 @@ size_of_encoded_value (unsigned char encoding)
     {
     case DW_EH_PE_absptr:
       return sizeof (void *);
+    case DW_EH_PE_udata1:
+      return 1;
     case DW_EH_PE_udata2:
       return 2;
     case DW_EH_PE_udata4:
@@ -184,6 +187,7 @@ read_encoded_value_with_base (unsigned char encodi
   union unaligned
     {
       void *ptr;
+      unsigned u1 __attribute__ ((mode (QI)));
       unsigned u2 __attribute__ ((mode (HI)));
       unsigned u4 __attribute__ ((mode (SI)));
       unsigned u8 __attribute__ ((mode (DI)));
@@ -227,6 +231,10 @@ read_encoded_value_with_base (unsigned char encodi
 	  }
 	  break;
 
+	case DW_EH_PE_udata1:
+	  result = u->u1;
+	  p += 1;
+	  break;
 	case DW_EH_PE_udata2:
 	  result = u->u2;
 	  p += 2;
Index: libgcc/unwind-compact.c
===================================================================
--- libgcc/unwind-compact.c	(revision 0)
+++ libgcc/unwind-compact.c	(revision 0)
@@ -0,0 +1,112 @@
+/* Copyright (C) 2012-2015 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   Under Section 7 of GPL version 3, you are granted additional
+   permissions described in the GCC Runtime Library Exception, version
+   3.1, as published by the Free Software Foundation.
+
+   You should have received a copy of the GNU General Public License and
+   a copy of the GCC Runtime Library Exception along with this program;
+   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "tconfig.h"
+#include "tsystem.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "dwarf2.h"
+#include "unwind.h"
+#define NO_BASE_OF_ENCODED_VALUE
+#include "unwind-pe.h"
+#include "unwind-dw2-fde.h"
+#include "unwind-compact.h"
+
+#ifdef MD_HAVE_COMPACT_EH
+/* Search a compact (v2) eh_frame_hdr table.  */
+
+struct compact_eh_index {
+    sword fn;
+    sword data;
+} __attribute__((packed, aligned(4)));
+
+static _Unwind_Ptr
+decode_index_ptr (const sword *p)
+{
+  return (*p & ~1) + (_Unwind_Ptr) p;
+}
+
+enum compact_entry_type
+_Unwind_Search_Compact_Eh_Hdr (void *pc, const unsigned char *hdr,
+			       struct compact_eh_bases *bases)
+{
+  int lo, hi, mid;
+  _Unwind_Ptr p;
+  _Unwind_Ptr nrec;
+  const struct compact_eh_index *ind;
+
+  bases->eh_encoding = hdr[1];
+  read_encoded_value_with_base (DW_EH_PE_absptr | DW_EH_PE_udata4, 0,
+				hdr + 4, &nrec);
+  if (nrec == 0)
+    return CET_not_found;
+
+  ind = (const struct compact_eh_index *)(hdr + 8);
+
+  lo = 0;
+  hi = nrec - 1;
+  while (1)
+    {
+      mid = (lo + hi) / 2;
+      p = decode_index_ptr (&ind[mid].fn);
+      if ((_Unwind_Ptr)pc < p)
+	{
+	  if (mid == 0)
+	    return CET_not_found;
+	  hi = mid - 1;
+	}
+      else
+	{
+	  if (mid == hi)
+	    break;
+	  p = decode_index_ptr (&ind[mid + 1].fn);
+	  if ((_Unwind_Ptr)pc < p)
+	    break;
+	  lo = mid + 1;
+	}
+    }
+
+  p = decode_index_ptr (&ind[mid].fn);
+  bases->func = (void *)p;
+  if ((ind[mid].fn & 1) == 0)
+    {
+      bases->entry = &ind[mid].data;
+      /* If we hit the terminating CANTUNWIND entry then assume
+         we're looking in the wrong table.  */
+      if (mid == (int)nrec - 1 && ind[mid].data == 0x015d5d01)
+	return CET_not_found;
+
+      return CET_inline;
+    }
+  else
+    {
+      p = decode_index_ptr (&ind[mid].data);
+      bases->entry = (const void *)p;
+      if (ind[mid].data & 1)
+	return CET_FDE;
+      else
+	return CET_outline;
+    }
+}
+
+#endif /* MD_HAVE_COMPACT_EH */
Index: libgcc/unwind-dw2-fde.c
===================================================================
--- libgcc/unwind-dw2-fde.c	(revision 226409)
+++ libgcc/unwind-dw2-fde.c	(working copy)
@@ -23,7 +23,8 @@ a copy of the GCC Runtime Library Exception along
 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 <http://www.gnu.org/licenses/>.  */
 
-#ifndef _Unwind_Find_FDE
+#ifndef _Unwind_Find_registered_Index
+#define _Unwind_Find_registered_Index _Unwind_Find_Index
 #include "tconfig.h"
 #include "tsystem.h"
 #include "coretypes.h"
@@ -35,7 +36,10 @@ see the files COPYING3 and COPYING.RUNTIME respect
 #include "unwind-pe.h"
 #include "unwind-dw2-fde.h"
 #include "gthr.h"
+#ifdef MD_HAVE_COMPACT_EH
+#include "unwind-compact.h"
 #endif
+#endif
 
 /* The unseen_objects list contains objects that have been registered
    but not yet categorized in any way.  The seen_objects list has had
@@ -71,22 +75,20 @@ static __gthread_mutex_t object_mutex;
 #endif
 #endif
 
-/* Called from crtbegin.o to register the unwind info for an object.  */
+/* Initialize OB and add to list of objects.  */
 
-void
-__register_frame_info_bases (const void *begin, struct object *ob,
-			     void *tbase, void *dbase)
+static void
+__register_frame_info_1 (const void *begin, struct object *ob,
+			  void *tbase, void *dbase, int header)
 {
-  /* If .eh_frame is empty, don't register at all.  */
-  if ((const uword *) begin == 0 || *(const uword *) begin == 0)
-    return;
-
   ob->pc_begin = (void *)-1;
   ob->tbase = tbase;
   ob->dbase = dbase;
   ob->u.single = begin;
   ob->s.i = 0;
   ob->s.b.encoding = DW_EH_PE_omit;
+  ob->s.b.header = header;
+  ob->s.b.mixed_encoding = header;
 #ifdef DWARF2_OBJECT_END_PTR_EXTENSION
   ob->fde_end = NULL;
 #endif
@@ -100,7 +102,35 @@ static __gthread_mutex_t object_mutex;
   __gthread_mutex_unlock (&object_mutex);
 }
 
+/* Called from crtbegin.o to register the unwind info for an object.  */
+
 void
+__register_frame_info_header_bases (const void *begin, struct object *ob,
+				    void *tbase, void *dbase)
+{
+  /* Only register compact EH frame headers.  */
+  if (begin == NULL || *(const char *) begin != 2)
+    return;
+
+#ifdef MD_HAVE_COMPACT_EH
+  __register_frame_info_1 (begin, ob, tbase, dbase, 1);
+#endif
+}
+
+/* Called from crtbegin.o to register the unwind info for an object.  */
+
+void
+__register_frame_info_bases (const void *begin, struct object *ob,
+			     void *tbase, void *dbase)
+{
+  /* If .eh_frame is empty, don't register at all.  */
+  if ((const uword *) begin == 0 || *(const uword *) begin == 0)
+    return;
+
+  __register_frame_info_1 (begin, ob, tbase, dbase, 0);
+}
+
+void
 __register_frame_info (const void *begin, struct object *ob)
 {
   __register_frame_info_bases (begin, ob, 0, 0);
@@ -951,9 +981,25 @@ binary_search_mixed_encoding_fdes (struct object *
   return NULL;
 }
 
-static const fde *
-search_object (struct object* ob, void *pc)
+static enum compact_entry_type
+search_object (struct object* ob, void *pc, struct compact_eh_bases *bases)
 {
+  const fde *f = NULL;
+
+#ifdef MD_HAVE_COMPACT_EH
+  if (ob->s.b.header)
+    {
+      const unsigned char *hdr = (const unsigned char *) ob->u.single;
+      if (ob->pc_begin == (void *) -1)
+	{
+	  const sword *first = (const sword *) (hdr + 8);
+	  ob->pc_begin = (void *)((*first & ~1) + (_Unwind_Ptr) first);
+	}
+
+      return _Unwind_Search_Compact_Eh_Hdr (pc, hdr, bases);
+    }
+#endif
+
   /* If the data hasn't been sorted, try to do this now.  We may have
      more memory available than last time we tried.  */
   if (! ob->s.b.sorted)
@@ -964,17 +1010,17 @@ binary_search_mixed_encoding_fdes (struct object *
 	 that we've not processed this object before.  A quick range
 	 check is in order.  */
       if (pc < ob->pc_begin)
-	return NULL;
+	return CET_not_found;
     }
 
   if (ob->s.b.sorted)
     {
       if (ob->s.b.mixed_encoding)
-	return binary_search_mixed_encoding_fdes (ob, pc);
+	f = binary_search_mixed_encoding_fdes (ob, pc);
       else if (ob->s.b.encoding == DW_EH_PE_absptr)
-	return binary_search_unencoded_fdes (ob, pc);
+	f = binary_search_unencoded_fdes (ob, pc);
       else
-	return binary_search_single_encoding_fdes (ob, pc);
+	f = binary_search_single_encoding_fdes (ob, pc);
     }
   else
     {
@@ -984,22 +1030,24 @@ binary_search_mixed_encoding_fdes (struct object *
 	  fde **p;
 	  for (p = ob->u.array; *p ; p++)
 	    {
-	      const fde *f = linear_search_fdes (ob, *p, pc);
+	      f = linear_search_fdes (ob, *p, pc);
 	      if (f)
-		return f;
+		break;
 	    }
-	  return NULL;
 	}
       else
-	return linear_search_fdes (ob, ob->u.single, pc);
+	f = linear_search_fdes (ob, ob->u.single, pc);
     }
+
+  bases->entry = f;
+  return f ? CET_FDE : CET_not_found;
 }
 
-const fde *
-_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
+enum compact_entry_type
+_Unwind_Find_registered_Index (void *pc, struct compact_eh_bases *bases)
 {
   struct object *ob;
-  const fde *f = NULL;
+  enum compact_entry_type t = CET_not_found;
 
   init_object_mutex_once ();
   __gthread_mutex_lock (&object_mutex);
@@ -1010,8 +1058,8 @@ binary_search_mixed_encoding_fdes (struct object *
   for (ob = seen_objects; ob; ob = ob->next)
     if (pc >= ob->pc_begin)
       {
-	f = search_object (ob, pc);
-	if (f)
+	t = search_object (ob, pc, bases);
+	if (t != CET_not_found)
 	  goto fini;
 	break;
       }
@@ -1022,7 +1070,7 @@ binary_search_mixed_encoding_fdes (struct object *
       struct object **p;
 
       unseen_objects = ob->next;
-      f = search_object (ob, pc);
+      t = search_object (ob, pc, bases);
 
       /* Insert the object into the classified list.  */
       for (p = &seen_objects; *p ; p = &(*p)->next)
@@ -1031,21 +1079,26 @@ binary_search_mixed_encoding_fdes (struct object *
       ob->next = *p;
       *p = ob;
 
-      if (f)
+      if (t != CET_not_found)
 	goto fini;
     }
 
  fini:
   __gthread_mutex_unlock (&object_mutex);
 
-  if (f)
+  if (t == CET_not_found)
+    return CET_not_found;
+
+  bases->tbase = ob->tbase;
+  bases->dbase = ob->dbase;
+
+  if (t == CET_FDE)
     {
+      const fde *f;
       int encoding;
       _Unwind_Ptr func;
 
-      bases->tbase = ob->tbase;
-      bases->dbase = ob->dbase;
-
+      f = bases->entry;
       encoding = ob->s.b.encoding;
       if (ob->s.b.mixed_encoding)
 	encoding = get_fde_encoding (f);
@@ -1054,5 +1107,21 @@ binary_search_mixed_encoding_fdes (struct object *
       bases->func = (void *) func;
     }
 
-  return f;
+  return t;
 }
+
+const fde *
+_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
+{
+  enum compact_entry_type type;
+  struct compact_eh_bases data;
+
+  type = _Unwind_Find_Index (pc, &data);
+  if (type != CET_FDE)
+    return NULL;
+
+  bases->tbase = data.tbase;
+  bases->dbase = data.dbase;
+  bases->func = data.func;
+  return (const fde *) data.entry;
+}
Index: libgcc/unwind-compact.h
===================================================================
--- libgcc/unwind-compact.h	(revision 0)
+++ libgcc/unwind-compact.h	(revision 0)
@@ -0,0 +1,39 @@
+/* Copyright (C) 2012-2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_UNWIND_COMPACT_H
+#define GCC_UNWIND_COMPACT_H
+
+enum compact_entry_type _Unwind_Search_Compact_Eh_Hdr (void *,
+    const unsigned char *, struct compact_eh_bases *);
+
+_Unwind_Reason_Code __gnu_compact_pr1 (int, _Unwind_Action,
+		  _Unwind_Exception_Class, struct _Unwind_Exception *,
+		  struct _Unwind_Context *);
+
+_Unwind_Reason_Code __gnu_compact_pr2 (int, _Unwind_Action,
+		  _Unwind_Exception_Class, struct _Unwind_Exception *,
+		  struct _Unwind_Context *);
+
+#define EH_FRAME_HEADER_VERSION 2
+#endif
+
Index: libgcc/unwind-dw2-fde.h
===================================================================
--- libgcc/unwind-dw2-fde.h	(revision 226409)
+++ libgcc/unwind-dw2-fde.h	(working copy)
@@ -53,10 +53,11 @@ struct object
       unsigned long sorted : 1;
       unsigned long from_array : 1;
       unsigned long mixed_encoding : 1;
+      unsigned long header : 1;
       unsigned long encoding : 8;
       /* ??? Wish there was an easy way to detect a 64-bit host here;
 	 we've got 32 bits left to play with...  */
-      unsigned long count : 21;
+      unsigned long count : 20;
     } b;
     size_t i;
   } s;
@@ -87,9 +88,12 @@ struct dwarf_eh_bases
   void *tbase;
   void *dbase;
   void *func;
+  unsigned char eh_encoding;
 };
 
 
+extern void __register_frame_info_header_bases (const void *, struct object *,
+						void *, void *);
 extern void __register_frame_info_bases (const void *, struct object *,
 					 void *, void *);
 extern void __register_frame_info (const void *, struct object *);
@@ -165,6 +169,24 @@ next_fde (const fde *f)
 
 extern const fde * _Unwind_Find_FDE (void *, struct dwarf_eh_bases *);
 
+struct compact_eh_bases {
+    void *tbase;
+    void *dbase;
+    void *func;
+    const void *entry;
+    unsigned char eh_encoding;
+};
+
+enum compact_entry_type {
+    CET_not_found,
+    CET_FDE,
+    CET_inline,
+    CET_outline
+};
+
+extern enum compact_entry_type _Unwind_Find_Index (void *,
+						   struct compact_eh_bases *);
+
 static inline int
 last_fde (struct object *obj __attribute__ ((__unused__)), const fde *f)
 {
Index: libgcc/crtstuff.c
===================================================================
--- libgcc/crtstuff.c	(revision 226409)
+++ libgcc/crtstuff.c	(working copy)
@@ -182,6 +182,9 @@ extern void __register_frame_info (const void *, s
 extern void __register_frame_info_bases (const void *, struct object *,
 					 void *, void *)
 				  TARGET_ATTRIBUTE_WEAK;
+extern void __register_frame_info_header_bases (const void *, struct object *,
+					 void *, void *)
+				  TARGET_ATTRIBUTE_WEAK;
 extern void *__deregister_frame_info (const void *)
 				     TARGET_ATTRIBUTE_WEAK;
 extern void *__deregister_frame_info_bases (const void *)
@@ -264,6 +267,10 @@ STATIC func_ptr __DTOR_LIST__[1]
 STATIC EH_FRAME_SECTION_CONST char __EH_FRAME_BEGIN__[]
      __attribute__((section(__LIBGCC_EH_FRAME_SECTION_NAME__), aligned(4)))
      = { };
+
+#ifdef MD_HAVE_COMPACT_EH
+extern char __GNU_EH_FRAME_HDR[] TARGET_ATTRIBUTE_WEAK;
+#endif /* MD_HAVE_COMPACT_EH */
 #endif /* USE_EH_FRAME_REGISTRY */
 
 #ifdef __LIBGCC_JCR_SECTION_NAME__
@@ -418,6 +425,12 @@ __do_global_dtors_aux (void)
 
 #ifdef USE_EH_FRAME_REGISTRY
 #ifdef CRT_GET_RFIB_DATA
+#ifdef MD_HAVE_COMPACT_EH
+  if (__register_frame_info_header_bases && __GNU_EH_FRAME_HDR &&
+      __GNU_EH_FRAME_HDR[0] > 1)
+    __deregister_frame_info_bases (__GNU_EH_FRAME_HDR);
+  else
+#endif /* MD_HAVE_COMPACT_EH */
   /* If we used the new __register_frame_info_bases interface,
      make sure that we deregister from the same place.  */
   if (__deregister_frame_info_bases)
@@ -464,6 +477,13 @@ frame_dummy (void)
   void *tbase, *dbase;
   tbase = 0;
   CRT_GET_RFIB_DATA (dbase);
+#ifdef MD_HAVE_COMPACT_EH
+  if (__register_frame_info_header_bases && __GNU_EH_FRAME_HDR &&
+      __GNU_EH_FRAME_HDR[0] > 1)
+    __register_frame_info_header_bases (__GNU_EH_FRAME_HDR, &object,
+					tbase, dbase);
+  else
+#endif /* MD_HAVE_COMPACT_EH */
   if (__register_frame_info_bases)
     __register_frame_info_bases (__EH_FRAME_BEGIN__, &object, tbase, dbase);
 #else
Index: libgcc/unwind-generic.h
===================================================================
--- libgcc/unwind-generic.h	(revision 226409)
+++ libgcc/unwind-generic.h	(working copy)
@@ -174,6 +174,8 @@ extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_C
 extern _Unwind_Ptr _Unwind_GetIPInfo (struct _Unwind_Context *, int *);
 extern void _Unwind_SetIP (struct _Unwind_Context *, _Unwind_Ptr);
 
+extern unsigned char _Unwind_GetEhEncoding (struct _Unwind_Context *);
+
 /* @@@ Retrieve the CFA of the given context.  */
 extern _Unwind_Word _Unwind_GetCFA (struct _Unwind_Context *);
 
Index: libgcc/config/t-eh-dw2-dip
===================================================================
--- libgcc/config/t-eh-dw2-dip	(revision 226409)
+++ libgcc/config/t-eh-dw2-dip	(working copy)
@@ -1,3 +1,3 @@
 # Use unwind-dw2-fde-dip.
-LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde-dip.c \
-  $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c
+LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-compact.c \
+  $(srcdir)/unwind-dw2-fde-dip.c $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c
Index: libgcc/config/mips/mips-unwind.h
===================================================================
--- libgcc/config/mips/mips-unwind.h	(revision 0)
+++ libgcc/config/mips/mips-unwind.h	(revision 0)
@@ -0,0 +1,182 @@
+/* Compact EH unwinding support for MIPS.
+   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#ifdef MD_HAVE_COMPACT_EH
+
+#define DWARF_SP_REGNO 29
+
+#if _MIPS_SIM == _ABIO32
+#define MIPS_EH_STACK_ALIGN 8
+#else
+#define MIPS_EH_STACK_ALIGN 16
+#endif
+
+#define VRF_0 32
+
+static int
+record_push (_Unwind_FrameState *fs, int reg, int offset)
+{
+  int idx = DWARF_REG_TO_UNWIND_COLUMN (reg);
+
+  offset -= dwarf_reg_size_table[idx];
+  fs->regs.reg[idx].how = REG_SAVED_OFFSET;
+  fs->regs.reg[idx].loc.offset = offset;
+  return offset;
+}
+
+static void
+record_cfa_adjustment (_Unwind_FrameState *fs, _uleb128_t val)
+{
+  int i;
+  fs->regs.cfa_offset += val;
+  /* In case we see an adjustment after pushes, it means that
+     the registers aren't saved at the top of the frame, probably
+     due to a varargs save area.  Adjust the recorded offsets.  */
+  for (i = 0; i < DWARF_FRAME_REGISTERS + 1; i++)
+    if (fs->regs.reg[i].how == REG_SAVED_OFFSET)
+      fs->regs.reg[i].loc.offset -= val;
+}
+
+/* Process the frame unwinding opcodes.  */
+
+static _Unwind_Reason_Code
+md_unwind_compact (struct _Unwind_Context *context ATTRIBUTE_UNUSED,
+		   _Unwind_FrameState *fs, const unsigned char **pp)
+{
+  unsigned char op;
+  _uleb128_t val;
+  int push_offset;
+  int i;
+  int n;
+  const unsigned char *p = *pp;
+
+  push_offset = 0;
+  fs->regs.cfa_how = CFA_REG_OFFSET;
+  fs->regs.cfa_reg = STACK_POINTER_REGNUM;
+  fs->regs.cfa_offset = 0;
+  fs->retaddr_column = 31;
+
+  while (1)
+    {
+      op = *(p++);
+
+      if (op < 0x40)
+	{
+	  /* Increment stack pointer.  */
+	  record_cfa_adjustment (fs, (op + 1) * MIPS_EH_STACK_ALIGN);
+	}
+      else if (op < 0x48)
+	{
+	  /* Push VR[16] to VR[16+x] and VR[31] */
+	  push_offset = record_push (fs, 31, push_offset);
+	  for (i = op & 7; i >= 0; i--)
+	    push_offset = record_push (fs, 16 + i, push_offset);
+	}
+      else if (op < 0x50)
+	{
+	  /* Push VR[16] to VR[16+x], VR[30] and VR[31] */
+	  push_offset = record_push (fs, 31, push_offset);
+	  push_offset = record_push (fs, 30, push_offset);
+	  for (i = op & 7; i >= 0; i--)
+	    push_offset = record_push (fs, 16 + i, push_offset);
+	}
+      else if (op < 0x58)
+	{
+	  /* Restore stack ponter from frame pointer */
+	  fs->regs.cfa_reg = (op & 7) + 16;
+	  fs->regs.cfa_offset = 0;
+	}
+      else if (op == 0x58)
+       	{
+	  /* Large SP increment.  */
+	  p = read_uleb128 (p, &val);
+	  record_cfa_adjustment (fs, (val + 129) * MIPS_EH_STACK_ALIGN);
+	}
+      else if (op == 0x59)
+	{
+	  /* Push VR[x] to VR[x+y] */
+	  op = *(p++);
+	  n = op >> 3;
+	  for (i = op & 7; i >= 0; i--)
+	    push_offset = record_push (fs, n + i, push_offset);
+	}
+      else if (op == 0x5a)
+	{
+	  /* Push VRF[x] to VRF[x+y] */
+	  op = *(p++);
+	  n = (op >> 3) + VRF_0;
+	  for (i = op & 7; i >= 0; i--)
+	    push_offset = record_push (fs, n + i, push_offset);
+	}
+      else if (op == 0x5b)
+	{
+	  /* Restore the CFA to stack pointer.  */
+	  fs->regs.cfa_reg = STACK_POINTER_REGNUM;
+	  fs->regs.cfa_offset = 0;
+	}
+
+      else if (op == 0x5c)
+	{
+	  /* Finish.  */
+	  *pp = p;
+	  return _URC_NO_REASON;
+	}
+      else if (op == 0x5d)
+	{
+	  /* No unwind.  */
+	  return _URC_END_OF_STACK;
+	}
+      else if (op == 0x5e)
+	{
+	  /* Restore SP from VR[30] */
+	  fs->regs.cfa_reg = 30;
+	  fs->regs.cfa_offset = 0;
+	}
+      else if (op == 0x5f)
+	{
+	  /* NOP */
+	}
+      else if (op >= 0x60 && op < 0x6c)
+	{
+	  /* Push VRF[20] to VRF[20 + x] */
+	  for (i = op & 0xf; i >= 0; i--)
+	    push_offset = record_push (fs, VRF_0 + 20 + i, push_offset);
+	}
+      else if (op >= 0x6c && op < 0x70)
+	{
+	  /* MIPS16 push VR[16], VR[17], VR[18+x]-VR[23], VR[31] */
+	  push_offset = record_push (fs, 31, push_offset);
+	  for (i = 23; i >= 18 + (op & 3); i--)
+	    push_offset = record_push (fs, i, push_offset);
+	  push_offset = record_push (fs, 17, push_offset);
+	  push_offset = record_push (fs, 16, push_offset);
+	}
+      else
+	{
+	  return _URC_FATAL_PHASE1_ERROR;
+	}
+    }
+}
+
+#endif /* MD_HAVE_COMPACT_EH */
Index: libgcc/config/mips/mips16.S
===================================================================
--- libgcc/config/mips/mips16.S	(revision 226409)
+++ libgcc/config/mips/mips16.S	(working copy)
@@ -624,6 +624,18 @@ CALL_STUB_NO_RET (__mips16_call_stub_10, 10)
    being called is 16 bits, in which case the copy is unnecessary;
    however, it's faster to always do the copy.  */
 
+#ifdef __GNU_COMPACT_EH__
+#define CALL_STUB_RET(NAME, CODE, MODE)	\
+STARTFN (NAME);				\
+	move    $18,$31;		\
+	STUB_ARGS_##CODE;		\
+	.set    noreorder;		\
+	jalr    $2;			\
+	move    $25,$2;			\
+	.set    reorder;		\
+	MOVE_##MODE##_RET (f, $18);	\
+	ENDFN (NAME)
+#else
 #define CALL_STUB_RET(NAME, CODE, MODE)					\
 CE_STARTFN (NAME);							\
 	.cfi_startproc;							\
@@ -642,6 +654,7 @@ CE_STARTFN (NAME);							\
 	MOVE_##MODE##_RET (f, $18);					\
 	.cfi_endproc;							\
 	CE_ENDFN (NAME)
+#endif
 
 /* First, instantiate the single-float set.  */
 
Index: libgcc/config/mips/linux-unwind.h
===================================================================
--- libgcc/config/mips/linux-unwind.h	(revision 226409)
+++ libgcc/config/mips/linux-unwind.h	(working copy)
@@ -26,6 +26,8 @@ see the files COPYING3 and COPYING.RUNTIME respect
 /* Do code reading to identify a signal frame, and set the frame
    state data appropriately.  See unwind-dw2.c for the structs.  */
 
+#include "config/mips/mips-unwind.h"
+
 #include <signal.h>
 #include <asm/unistd.h>
 
Index: libgcc/unwind-compat.c
===================================================================
--- libgcc/unwind-compat.c	(revision 226409)
+++ libgcc/unwind-compat.c	(working copy)
@@ -206,4 +206,14 @@ _Unwind_SetIP (struct _Unwind_Context *context, _U
   return __libunwind_Unwind_SetIP (context, val);
 }
 symver (_Unwind_SetIP, GCC_3.0);
+
+extern unsigned char __libunwind_Unwind_GetEhEncoding
+  (struct _Unwind_Context *);
+
+unsigned char
+_Unwind_GetEhEncoding (struct _Unwind_Context *context)
+{
+  return __libunwind_Unwind_GetEhEncoding (context, val);
+}
+symver (_Unwind_GetEhEncoding, GCC_3.0);
 #endif
Index: libgcc/unwind-dw2.c
===================================================================
--- libgcc/unwind-dw2.c	(revision 226409)
+++ libgcc/unwind-dw2.c	(working copy)
@@ -153,6 +153,7 @@ static unsigned char dwarf_reg_size_table[__LIBGCC
 union unaligned
 {
   void *p;
+  unsigned u1 __attribute__ ((mode (QI)));
   unsigned u2 __attribute__ ((mode (HI)));
   unsigned u4 __attribute__ ((mode (SI)));
   unsigned u8 __attribute__ ((mode (DI)));
@@ -396,6 +397,12 @@ _Unwind_GetTextRelBase (struct _Unwind_Context *co
 {
   return (_Unwind_Ptr) context->bases.tbase;
 }
+
+unsigned char
+_Unwind_GetEhEncoding (struct _Unwind_Context *context)
+{
+  return context->bases.eh_encoding;
+}
 #endif
 
 #include "md-unwind-support.h"
@@ -1219,6 +1226,75 @@ execute_cfa_program (const unsigned char *insn_ptr
     }
 }
 \f
+#ifdef MD_HAVE_COMPACT_EH
+static _Unwind_Reason_Code
+__gnu_compact_pr1 (int version ATTRIBUTE_UNUSED,
+		  _Unwind_Action actions ATTRIBUTE_UNUSED,
+		  _Unwind_Exception_Class exception_class ATTRIBUTE_UNUSED,
+		  struct _Unwind_Exception *ue_header ATTRIBUTE_UNUSED,
+		  struct _Unwind_Context *context ATTRIBUTE_UNUSED)
+{
+  return _URC_CONTINUE_UNWIND;
+}
+
+/* The C++ EH routines need to live in the C++ runtime.  These should
+   be pulled in via the unwinding tables when needed.  */
+extern _Unwind_Reason_Code __gnu_compact_pr2 (int, _Unwind_Action,
+  _Unwind_Exception_Class, struct _Unwind_Exception *,
+  struct _Unwind_Context *) TARGET_ATTRIBUTE_WEAK;
+
+/* Invoke the Compact EH unwinder.  */
+
+static _Unwind_Reason_Code
+uw_frame_state_compact (struct _Unwind_Context *context,
+			_Unwind_FrameState *fs,
+			enum compact_entry_type entry_type,
+		       	struct compact_eh_bases *bases)
+{
+  const unsigned char *p;
+  unsigned int pr_index;
+  _Unwind_Ptr personality;
+  unsigned char buf[4];
+  _Unwind_Reason_Code rc;
+
+  p = bases->entry;
+  pr_index = *(p++);
+  switch (pr_index) {
+  case 0:
+      p = read_encoded_value (context, bases->eh_encoding, p, &personality);
+      fs->personality = (_Unwind_Personality_Fn) personality;
+      break;
+  case 1:
+      fs->personality = __gnu_compact_pr1;
+      break;
+  case 2:
+      fs->personality = __gnu_compact_pr2;
+      break;
+  default:
+      fs->personality = NULL;
+  }
+
+  if (!fs->personality)
+    return _URC_FATAL_PHASE1_ERROR;
+
+  if (entry_type == CET_inline)
+    {
+      memcpy(buf, p, 3);
+      buf[3] = md_unwind_compact_opcode_finish;
+      p = buf;
+    }
+
+  rc = md_unwind_compact (context, fs, &p);
+  if (rc != _URC_NO_REASON)
+      return rc;
+
+  if (entry_type == CET_outline)
+    context->lsda = (void *)(_Unwind_Internal_Ptr) p;
+
+  return _URC_NO_REASON;
+}
+#endif
+
 /* Given the _Unwind_Context CONTEXT for a stack frame, look up the FDE for
    its caller and decode it into FS.  This function also sets the
    args_size and lsda members of CONTEXT, as they are really information
@@ -1238,8 +1314,27 @@ uw_frame_state_for (struct _Unwind_Context *contex
   if (context->ra == 0)
     return _URC_END_OF_STACK;
 
+#ifdef MD_HAVE_COMPACT_EH
+    {
+      struct compact_eh_bases bases;
+      enum compact_entry_type type;
+      type = _Unwind_Find_Index (context->ra + _Unwind_IsSignalFrame (context)
+				 - 1, &bases);
+      context->bases.tbase = bases.tbase;
+      context->bases.dbase = bases.dbase;
+      context->bases.func = bases.func;
+      context->bases.eh_encoding = bases.eh_encoding;
+      if (type == CET_inline || type == CET_outline)
+	return uw_frame_state_compact (context, fs, type, &bases);
+      if (type == CET_FDE)
+	fde = bases.entry;
+      else
+	fde = NULL;
+    }
+#else
   fde = _Unwind_Find_FDE (context->ra + _Unwind_IsSignalFrame (context) - 1,
 			  &context->bases);
+#endif
   if (fde == NULL)
     {
 #ifdef MD_FALLBACK_FRAME_STATE_FOR
@@ -1559,9 +1654,6 @@ uw_init_context_1 (struct _Unwind_Context *context
   if (!ASSUME_EXTENDED_UNWIND_CONTEXT)
     context->flags = EXTENDED_CONTEXT_BIT;
 
-  code = uw_frame_state_for (context, &fs);
-  gcc_assert (code == _URC_NO_REASON);
-
 #if __GTHREADS
   {
     static __gthread_once_t once_regsizes = __GTHREAD_ONCE_INIT;
@@ -1574,6 +1666,9 @@ uw_init_context_1 (struct _Unwind_Context *context
     init_dwarf_reg_size_table ();
 #endif
 
+  code = uw_frame_state_for (context, &fs);
+  gcc_assert (code == _URC_NO_REASON);
+
   /* Force the frame state to use the known cfa value.  */
   _Unwind_SetSpColumn (context, outer_cfa, &sp_slot);
   fs.regs.cfa_how = CFA_REG_OFFSET;
@@ -1710,6 +1805,7 @@ alias (_Unwind_Resume);
 alias (_Unwind_Resume_or_Rethrow);
 alias (_Unwind_SetGR);
 alias (_Unwind_SetIP);
+alias (_Unwind_GetEhEncoding);
 #endif
 
 #endif /* !USING_SJLJ_EXCEPTIONS */
Index: gcc/config.in
===================================================================
--- gcc/config.in	(revision 226409)
+++ gcc/config.in	(working copy)
@@ -1162,6 +1162,13 @@
 #endif
 
 
+/* Define if your assembler supports generation of .eh_frame_entry from CFI
+   directives. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_GAS_EH_FRAME_ENTRY
+#endif
+
+
 /* Define if your assembler supports @gnu_unique_object. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_GAS_GNU_UNIQUE_OBJECT
Index: gcc/doc/tm.texi
===================================================================
--- gcc/doc/tm.texi	(revision 226409)
+++ gcc/doc/tm.texi	(working copy)
@@ -8169,6 +8169,10 @@ systems.  This macro is used in @code{assemble_nam
 Given a symbol @var{name}, perform same mangling as @code{varasm.c}'s @code{assemble_name}, but in memory rather than to a file stream, returning result as an @code{IDENTIFIER_NODE}.  Required for correct LTO symtabs.  The default implementation calls the @code{TARGET_STRIP_NAME_ENCODING} hook and then prepends the @code{USER_LABEL_PREFIX}, if any.
 @end deftypefn
 
+@deftypefn {Target Hook} void TARGET_OUTPUT_CFI_ENDPROC (void)
+Emit the @code{.cfi_endproc} directive at the end of a function, and optionally additional exception handling information for it.
+@end deftypefn
+
 @defmac ASM_OUTPUT_SYMBOL_REF (@var{stream}, @var{sym})
 A C statement (sans semicolon) to output a reference to
 @code{SYMBOL_REF} @var{sym}.  If not defined, @code{assemble_name}
Index: gcc/doc/invoke.texi
===================================================================
--- gcc/doc/invoke.texi	(revision 226409)
+++ gcc/doc/invoke.texi	(working copy)
@@ -814,7 +814,7 @@ Objective-C and Objective-C++ Dialects}.
 -mbranch-cost=@var{num}  -mbranch-likely  -mno-branch-likely @gol
 -mfp-exceptions -mno-fp-exceptions @gol
 -mvr4130-align -mno-vr4130-align -msynci -mno-synci @gol
--mrelax-pic-calls -mno-relax-pic-calls -mmcount-ra-address}
+-mrelax-pic-calls -mno-relax-pic-calls -mmcount-ra-address -mno-compact-eh}
 
 @emph{MMIX Options}
 @gccoptlist{-mlibfuncs  -mno-libfuncs  -mepsilon  -mno-epsilon  -mabi=gnu @gol
@@ -18029,6 +18029,13 @@ if @var{ra-address} is nonnull.
 
 The default is @option{-mno-mcount-ra-address}.
 
+@item -mno-compact-eh
+@itemx -mcompact-eh
+@opindex mno-compact-eh
+@opindex mcompact-eh
+Disable generation of compact EH frame tables.
+@option{-mcompact-eh} is the default where supported.
+
 @end table
 
 @node MMIX Options
Index: gcc/doc/tm.texi.in
===================================================================
--- gcc/doc/tm.texi.in	(revision 226409)
+++ gcc/doc/tm.texi.in	(working copy)
@@ -5760,6 +5760,8 @@ systems.  This macro is used in @code{assemble_nam
 
 @hook TARGET_MANGLE_ASSEMBLER_NAME
 
+@hook TARGET_OUTPUT_CFI_ENDPROC
+
 @defmac ASM_OUTPUT_SYMBOL_REF (@var{stream}, @var{sym})
 A C statement (sans semicolon) to output a reference to
 @code{SYMBOL_REF} @var{sym}.  If not defined, @code{assemble_name}
Index: gcc/dwarf2asm.c
===================================================================
--- gcc/dwarf2asm.c	(revision 226409)
+++ gcc/dwarf2asm.c	(working copy)
@@ -84,6 +84,22 @@ dw2_asm_output_data_raw (int size, unsigned HOST_W
     }
 }
 
+/* Output a comment.  */
+void
+dw2_asm_output_comment (const char *comment, ...)
+{
+  va_list ap;
+
+  va_start (ap, comment);
+
+  if (flag_debug_asm && comment)
+    {
+      fprintf (asm_out_file, "\t%s ", ASM_COMMENT_START);
+      vfprintf (asm_out_file, comment, ap);
+    }
+  fputc ('\n', asm_out_file);
+}
+
 /* Output an immediate constant in a given SIZE in bytes.  */
 
 void
@@ -411,7 +427,7 @@ eh_data_format_name (int format)
 #if HAVE_DESIGNATED_INITIALIZERS
   __extension__ static const char * const format_names[256] = {
 #else
-  switch (format) {
+  switch (format & ~GCC_DW_EH_PE_special) {
 #endif
 
   S(DW_EH_PE_absptr, "absolute")
@@ -548,6 +564,7 @@ eh_data_format_name (int format)
 #if HAVE_DESIGNATED_INITIALIZERS
   };
 
+  format &= ~GCC_DW_EH_PE_special;
   gcc_assert (format >= 0 && format < 0x100 && format_names[format]);
 
   return format_names[format];
@@ -754,6 +771,99 @@ dw2_asm_output_delta_uleb128 (const char *lab1 ATT
   va_end (ap);
 }
 
+/* Output a compact EH region length entry. */
+void
+dw2_asm_output_compact_region_length (const char *lab1 ATTRIBUTE_UNUSED,
+				      const char *lab2 ATTRIBUTE_UNUSED,
+				      bool setbit0,
+				      const char *comment, ...)
+{
+  va_list ap;
+
+  va_start (ap, comment);
+
+#ifdef HAVE_AS_LEB128
+  fputs ("\t.uleb128 (", asm_out_file);
+  assemble_name (asm_out_file, lab1);
+  fputs ("-", asm_out_file);
+  assemble_name (asm_out_file, lab2);
+  fputs (")", asm_out_file);
+  if (setbit0)
+    fputs ("|1", asm_out_file);
+#else
+  gcc_unreachable ();
+#endif
+
+  if (flag_debug_asm && comment)
+    {
+      fprintf (asm_out_file, "\t%s ", ASM_COMMENT_START);
+      vfprintf (asm_out_file, comment, ap);
+    }
+  fputc ('\n', asm_out_file);
+
+  va_end (ap);
+}
+
+/* Output a compact EH landing pad entry.  */
+
+void
+dw2_asm_output_compact_landing_pad (const char *lab1 ATTRIBUTE_UNUSED,
+				    const char *lab2 ATTRIBUTE_UNUSED,
+				    const char *comment, ...)
+{
+  va_list ap;
+
+  va_start (ap, comment);
+
+#ifdef HAVE_AS_LEB128
+  fputs ("\t.sleb128 (", asm_out_file);
+  assemble_name (asm_out_file, lab1);
+  fputs ("-(", asm_out_file);
+  assemble_name (asm_out_file, lab2);
+  fputs ("))", asm_out_file);
+#else
+  gcc_unreachable ();
+#endif
+
+  if (flag_debug_asm && comment)
+    {
+      fprintf (asm_out_file, "\t%s ", ASM_COMMENT_START);
+      vfprintf (asm_out_file, comment, ap);
+    }
+  fputc ('\n', asm_out_file);
+
+  va_end (ap);
+}
+
+/* Output a compact EH action/chain pair.  */
+
+void
+dw2_asm_output_compact_ac_pair_sleb128 (int action, int chain_value,
+					const char *comment, ...)
+{
+  va_list ap;
+
+  va_start (ap, comment);
+
+#ifdef HAVE_AS_LEB128
+  fputs ("\t.sleb128 ", asm_out_file);
+  fputc ('(', asm_out_file);
+  fprintf (asm_out_file, "%#x", chain_value);
+  fputs ("<<2)|", asm_out_file);
+  fprintf (asm_out_file, "%#x", action);
+#else
+  gcc_unreachable ();
+#endif
+
+  if (flag_debug_asm && comment)
+    {
+      fprintf (asm_out_file, "\t%s ", ASM_COMMENT_START);
+      vfprintf (asm_out_file, comment, ap);
+    }
+  fputc ('\n', asm_out_file);
+
+  va_end (ap);
+}
 #if 0
 
 void
Index: gcc/dwarf2asm.h
===================================================================
--- gcc/dwarf2asm.h	(revision 226409)
+++ gcc/dwarf2asm.h	(working copy)
@@ -24,6 +24,9 @@ extern void dw2_assemble_integer (int, rtx);
 
 extern void dw2_asm_output_data_raw (int, unsigned HOST_WIDE_INT);
 
+extern void dw2_asm_output_comment (const char *, ...)
+     ATTRIBUTE_NULL_PRINTF_1;
+
 extern void dw2_asm_output_data (int, unsigned HOST_WIDE_INT,
 				 const char *, ...)
      ATTRIBUTE_NULL_PRINTF_3;
@@ -70,6 +73,17 @@ extern void dw2_asm_output_delta_uleb128 (const ch
 					  const char *, ...)
      ATTRIBUTE_NULL_PRINTF_3;
 
+extern void dw2_asm_output_compact_region_length (const char *, const char *,
+					          bool, const char *, ...)
+     ATTRIBUTE_NULL_PRINTF_4;
+
+extern void dw2_asm_output_compact_landing_pad (const char *, const char *,
+						const char *, ...)
+     ATTRIBUTE_NULL_PRINTF_3;
+
+extern void dw2_asm_output_compact_ac_pair_sleb128 (int, int, const char *, ...)
+     ATTRIBUTE_NULL_PRINTF_3;
+
 extern int size_of_uleb128 (unsigned HOST_WIDE_INT);
 extern int size_of_sleb128 (HOST_WIDE_INT);
 extern int size_of_encoded_value (int);
@@ -89,4 +103,8 @@ extern void dw2_asm_output_delta_sleb128 (const ch
      ATTRIBUTE_NULL_PRINTF_3;
 #endif
 
+/* Provide a bit that can be used to mark an EH encoding as requiring
+   special handling.  */
+#define GCC_DW_EH_PE_special 0x100
+
 #endif /* GCC_DWARF2ASM_H */
Index: gcc/targhooks.c
===================================================================
--- gcc/targhooks.c	(revision 226409)
+++ gcc/targhooks.c	(working copy)
@@ -1926,4 +1926,10 @@ can_use_doloop_if_innermost (const widest_int &, c
   return loop_depth == 1;
 }
 
+void
+default_asm_output_cfi_endproc (void)
+{
+  fprintf (asm_out_file, "\t.cfi_endproc\n");
+}
+
 #include "gt-targhooks.h"
Index: gcc/targhooks.h
===================================================================
--- gcc/targhooks.h	(revision 226409)
+++ gcc/targhooks.h	(working copy)
@@ -197,6 +197,7 @@ extern bool default_class_likely_spilled_p (reg_cl
 extern unsigned char default_class_max_nregs (reg_class_t, machine_mode);
 
 extern enum unwind_info_type default_debug_unwind_info (void);
+extern void default_asm_output_cfi_endproc (void);
 
 extern void default_canonicalize_comparison (int *, rtx *, rtx *, bool);
 
Index: gcc/config/mips/sde.h
===================================================================
--- gcc/config/mips/sde.h	(revision 226409)
+++ gcc/config/mips/sde.h	(working copy)
@@ -18,6 +18,11 @@ You should have received a copy of the GNU General
 along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
 
+#ifdef HAVE_GAS_EH_FRAME_ENTRY
+#undef MIPS_COMPACT_EH_PCREL
+#define MIPS_COMPACT_EH_PCREL 1
+#endif
+
 #undef DRIVER_SELF_SPECS
 #define DRIVER_SELF_SPECS						\
   /* Set the ISA for the default multilib.  */				\
Index: gcc/config/mips/mips-protos.h
===================================================================
--- gcc/config/mips/mips-protos.h	(revision 226409)
+++ gcc/config/mips/mips-protos.h	(working copy)
@@ -255,6 +255,7 @@ extern void mips_pop_asm_switch (struct mips_asm_s
 extern void mips_output_external (FILE *, tree, const char *);
 extern void mips_output_ascii (FILE *, const char *, size_t);
 extern const char *mips_output_tls_reloc_directive (rtx *);
+extern void mips_fixup_cfi_sections (void);
 extern void mips_output_aligned_decl_common (FILE *, tree, const char *,
 					     unsigned HOST_WIDE_INT,
 					     unsigned int);
Index: gcc/config/mips/linux.h
===================================================================
--- gcc/config/mips/linux.h	(revision 226409)
+++ gcc/config/mips/linux.h	(working copy)
@@ -47,3 +47,5 @@ along with GCC; see the file COPYING3.  If not see
 #define GNU_USER_DYNAMIC_LINKERN32 \
   CHOOSE_DYNAMIC_LINKER (GLIBC_DYNAMIC_LINKERN32, UCLIBC_DYNAMIC_LINKERN32, \
                          BIONIC_DYNAMIC_LINKERN32, MUSL_DYNAMIC_LINKERN32)
+
+#define MD_HAVE_COMPACT_EH 1
Index: gcc/config/mips/elf.h
===================================================================
--- gcc/config/mips/elf.h	(revision 226409)
+++ gcc/config/mips/elf.h	(working copy)
@@ -48,3 +48,5 @@ along with GCC; see the file COPYING3.  If not see
 #define ENDFILE_SPEC "crtend%O%s crtn%O%s"
 
 #define NO_IMPLICIT_EXTERN_C 1
+
+#define MD_HAVE_COMPACT_EH 1
Index: gcc/config/mips/mips.opt
===================================================================
--- gcc/config/mips/mips.opt	(revision 226409)
+++ gcc/config/mips/mips.opt	(working copy)
@@ -99,6 +99,10 @@ Enum(mips_code_readable_setting) String(pcrel) Val
 EnumValue
 Enum(mips_code_readable_setting) String(no) Value(CODE_READABLE_NO)
 
+mcompact-eh
+Target Var(TARGET_COMPACT_EH) Init(1)
+Use compact exception unwinding tables.
+
 mdivide-breaks
 Target Report RejectNegative Mask(DIVIDE_BREAKS)
 Use branch-and-break sequences to check for integer divide by zero
Index: gcc/config/mips/mips.c
===================================================================
--- gcc/config/mips/mips.c	(revision 226409)
+++ gcc/config/mips/mips.c	(working copy)
@@ -77,6 +77,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cgraph.h"
 #include "builtins.h"
 #include "rtl-iter.h"
+#include "except.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -693,6 +694,8 @@ static mips_one_only_stub *mips16_rdhwr_stub;
 static mips_one_only_stub *mips16_get_fcsr_stub;
 static mips_one_only_stub *mips16_set_fcsr_stub;
 
+static bool done_cfi_sections;
+
 /* Index R is the smallest register class that contains register R.  */
 const enum reg_class mips_regno_to_class[FIRST_PSEUDO_REGISTER] = {
   LEA_REGS,        LEA_REGS,        M16_STORE_REGS,  V1_REG,
@@ -6669,6 +6672,40 @@ mips_start_unique_function (const char *name)
   putc ('\n', asm_out_file);
 }
 
+/* The LTO frontend only enables exceptions when it sees a function that
+   uses it.  This changes the return value of dwarf2out_do_frame, so we
+   have to check before every function.  */
+
+void
+mips_fixup_cfi_sections (void)
+{
+#ifdef MD_HAVE_COMPACT_EH
+  if (done_cfi_sections)
+    return;
+
+  if (!TARGET_COMPACT_EH)
+    return;
+
+  /* Output a .cfi_sections directive.  */
+  if (dwarf2out_do_frame ())
+    {
+      if (flag_unwind_tables || flag_exceptions)
+	{
+	  if (write_symbols == DWARF2_DEBUG
+	      || write_symbols == VMS_AND_DWARF2_DEBUG)
+	    fprintf (asm_out_file,
+		     "\t.cfi_sections .debug_frame, .eh_frame_entry\n");
+	  else
+	    fprintf (asm_out_file, "\t.cfi_sections .eh_frame_entry\n");
+	}
+      else
+	fprintf (asm_out_file, "\t.cfi_sections .debug_frame\n");
+      done_cfi_sections = true;
+    }
+#endif
+}
+
+
 /* Start a definition of function NAME.  MIPS16_P indicates whether the
    function contains MIPS16 code.  */
 
@@ -7252,7 +7289,13 @@ mips16_build_call_stub (rtx retval, rtx *fn_ptr, r
 
 	  /* "Save" $sp in itself so we don't use the fake CFA.
 	     This is: DW_CFA_val_expression r29, { DW_OP_reg29 }.  */
-	  fprintf (asm_out_file, "\t.cfi_escape 0x16,29,1,0x6d\n");
+	  if (dwarf2out_do_frame ()
+	      && (flag_unwind_tables || flag_exceptions))
+	    /* "Save" $sp in itself so we don't use the fake CFA.
+	       This is: DW_CFA_val_expression r29, { DW_OP_reg29 }.  */
+	    fprintf (asm_out_file, "\t.cfi_fde_data 0x5b\n");
+	  else
+	    fprintf (asm_out_file, "\t.cfi_escape 0x16,29,1,0x6d\n");
 
 	  /* Save the return address in $18.  The stub's caller knows
 	     that $18 might be clobbered, even though $18 is usually
@@ -8850,6 +8893,19 @@ mips_function_rodata_section (tree decl)
   return data_section;
 }
 
+/* Implement TARGET_ASM_INIT_SECTIONS.  */
+
+static void
+mips_asm_init_sections (void)
+{
+  if (TARGET_COMPACT_EH)
+    {
+      /* Let the assembler decide where to put the LSDA.  */
+      exception_section = get_unnamed_section (0, output_section_asm_op,
+					       "\t.cfi_inline_lsda 2");
+    }
+}
+
 /* Implement TARGET_IN_SMALL_DATA_P.  */
 
 static bool
@@ -8860,6 +8916,13 @@ mips_in_small_data_p (const_tree decl)
   if (TREE_CODE (decl) == STRING_CST || TREE_CODE (decl) == FUNCTION_DECL)
     return false;
 
+  /* Place eh-related data in sdata so that we can use a gprel32 reloc
+     to access it.  */
+  if (TARGET_64BIT
+      && TREE_CODE (decl) == VAR_DECL && DECL_NAME (decl)
+      && strncmp (IDENTIFIER_POINTER (DECL_NAME (decl)), "DW.ref.", 7) == 0)
+    return true;
+
   /* We don't yet generate small-data references for -mabicalls
      or VxWorks RTP code.  See the related -G handling in
      mips_option_override.  */
@@ -9354,6 +9417,8 @@ mips_file_start (void)
 {
   default_file_start ();
 
+  done_cfi_sections = false;
+
   /* Generate a special section to describe the ABI switches used to
      produce the resultant binary.  */
 
@@ -12175,6 +12240,175 @@ mips_expand_epilogue (bool sibcall_p)
       emit_insn_before (gen_mips_ehb (), insn);
     }
 }
+
+/* Add an opcode to *FDE to describe the necessary pushes of r30/r31, if
+   any.  */
+static void
+add_fp_ra_push (vec<uchar, va_gc> **fde)
+{
+  bool fp_needed = BITSET_P (cfun->machine->frame.mask, 30);
+  if (!BITSET_P (cfun->machine->frame.mask, RETURN_ADDR_REGNUM)
+      && !fp_needed)
+    return;
+
+  vec_safe_push (*fde, (uchar) 0x59);
+  if (fp_needed)
+    vec_safe_push (*fde, (uchar) ((30 << 3) | 1));
+  else
+    vec_safe_push (*fde, uchar (31 << 3));
+}
+
+/* Add an opcode to *FDE to describe an adjustment of the CFA register by
+   AMOUNT, given an assumed alignment ALIGN.  */
+static void
+mips_fdedata_cfaadjust (vec <uchar, va_gc> **fde,
+			HOST_WIDE_INT amount, int align)
+{
+  if (amount == 0)
+    return;
+
+  gcc_assert (amount % align == 0);
+  if (amount >= align * 129)
+    {
+      vec_safe_push (*fde, (uchar) 0x58);
+      push_uleb128 (fde, amount / align - 129);
+    }
+  else if (amount >= align * 65)
+    {
+      vec_safe_push (*fde, (uchar) 0x3f);
+      vec_safe_push (*fde, (uchar) (amount / align - 64));
+    }
+  else
+    vec_safe_push (*fde, (uchar) (amount / align - 1));
+}
+
+/* Implements TARGET_ASM_OUTPUT_CFI_ENDPROC.  Emit the .cfi_endproc
+   directive, and when emitting unwind information, also emit the
+   .cfi_fde_data directive.  */
+static void
+mips_cfi_endproc (void)
+{
+  int align = TARGET_NEWABI ? 16 : 8;
+  vec<uchar, va_gc> *fde = NULL;
+  int i, len, n;
+  HOST_WIDE_INT total, push_base;
+  bool any_saved = false;
+
+  if (!TARGET_COMPACT_EH
+      || (!flag_unwind_tables && !flag_exceptions)
+      || flag_asynchronous_unwind_tables)
+    {
+      fprintf (asm_out_file, ".cfi_endproc\n");
+      return;
+    }
+
+  fprintf (asm_out_file, "\t.cfi_fde_data ");
+
+  total = cfun->machine->frame.total_size;
+  if (cfun->machine->frame.num_fp > 0)
+    push_base = cfun->machine->frame.fp_sp_offset + UNITS_PER_HWFPVALUE;
+  else if (cfun->machine->frame.num_gp > 0)
+    push_base = cfun->machine->frame.gp_sp_offset + UNITS_PER_WORD;
+  else
+    push_base = total;
+
+  if (frame_pointer_needed)
+    {
+      if (HARD_FRAME_POINTER_REGNUM == 30)
+	vec_safe_push (fde, (uchar) 0x5E);
+      else
+	{
+	  int t = HARD_FRAME_POINTER_REGNUM - 16;
+	  gcc_assert (t < 8);
+	  vec_safe_push (fde, (uchar) (0x50 + t));
+	}
+      total -= cfun->machine->frame.hard_frame_pointer_offset;
+      push_base -= cfun->machine->frame.hard_frame_pointer_offset;
+    }
+
+  if (HARD_FRAME_POINTER_REGNUM == 30 && frame_pointer_needed)
+    gcc_assert (BITSET_P (cfun->machine->frame.mask, 30));
+
+  mips_fdedata_cfaadjust (&fde, push_base, align);
+  total -= push_base;
+
+  n = 0;
+  for (i = 31; i > 19; i--)
+    {
+      bool isset = BITSET_P (cfun->machine->frame.fmask, i);
+      if (isset)
+	n++;
+      if ((i == 20 || !isset) && n > 0)
+	{
+	  int first = i + (isset ? 0 : 1);
+	  if (first == 20)
+	    {
+	      if (n > 8)
+		vec_safe_push (fde, (uchar) (0x68 + n - 9));
+	      else
+		vec_safe_push (fde, (uchar) (0x60 + n - 1));
+	    }
+	  else
+	    {
+	      if (n > 8)
+		{
+		  vec_safe_push (fde, (uchar) 0x5a);
+		  vec_safe_push (fde, (uchar) (((first + 8) << 3) | (n - 9)));
+		  n = 8;
+		}
+	      vec_safe_push (fde, (uchar) 0x5a);
+	      vec_safe_push (fde, (uchar) ((first << 3) | (n - 1)));
+	    }
+	  n = 0;
+	}
+    }
+  gcc_assert (n == 0);
+
+  for (i = 23; i > 0; i--)
+    {
+      bool isset = BITSET_P (cfun->machine->frame.mask, i);
+      if (isset)
+	n++;
+      if ((i == 16 || i == 1 || !isset) && n > 0)
+	{
+	  int first = i + (isset ? 0 : 1);
+	  if (first == 16 && !any_saved
+	      && BITSET_P (cfun->machine->frame.mask, RETURN_ADDR_REGNUM))
+	    {
+	      if (BITSET_P (cfun->machine->frame.mask, 30))
+		vec_safe_push (fde, (uchar) (0x48 + n - 1));
+	      else
+		vec_safe_push (fde, (uchar) (0x40 + n - 1));
+	    }
+	  else
+	    {
+	      if (!any_saved)
+		add_fp_ra_push (&fde);
+	      vec_safe_push (fde, (uchar) 0x59);
+	      vec_safe_push (fde, (uchar) ((first << 3) | (n - 1)));
+	    }
+	  any_saved = true;
+	  n = 0;
+	}
+    }
+  gcc_assert (n == 0);
+
+  if (!any_saved)
+    add_fp_ra_push (&fde);
+
+  /* Skip varargs save area and suchlike.  */
+  mips_fdedata_cfaadjust (&fde, total, align);
+
+  /* The assembler appends a "Finish" opcode for out-of-line entries;
+     the unwind code (uw_frame_state_compact) for inline entries.  */
+
+  len = vec_safe_length (fde);
+  for (i = 0; i < len; i++)
+    fprintf (asm_out_file, "0x%x%s", (*fde)[i],
+	     i + 1 == len ? "" : ",");
+  fputc ('\n', asm_out_file);
+  fprintf (asm_out_file, "\t.cfi_endproc\n");
+}
 \f
 /* Return nonzero if this function is known to have a null epilogue.
    This allows the optimizer to omit jumps to jumps if no stack
@@ -17454,6 +17688,10 @@ mips_option_override (void)
   SUBTARGET_OVERRIDE_OPTIONS;
 #endif
 
+#if !(defined(MD_HAVE_COMPACT_EH) && defined(HAVE_GAS_EH_FRAME_ENTRY))
+  TARGET_COMPACT_EH = 0;
+#endif
+
   /* MIPS16 and microMIPS cannot coexist.  */
   if (TARGET_MICROMIPS && TARGET_MIPS16)
     error ("unsupported combination: %s", "-mips16 -mmicromips");
@@ -19617,6 +19855,8 @@ mips_ira_change_pseudo_allocno_class (int regno, r
 #define TARGET_ASM_SELECT_RTX_SECTION mips_select_rtx_section
 #undef TARGET_ASM_FUNCTION_RODATA_SECTION
 #define TARGET_ASM_FUNCTION_RODATA_SECTION mips_function_rodata_section
+#undef TARGET_ASM_INIT_SECTIONS
+#define TARGET_ASM_INIT_SECTIONS mips_asm_init_sections
 
 #undef TARGET_SCHED_INIT
 #define TARGET_SCHED_INIT mips_sched_init
@@ -19824,6 +20064,9 @@ mips_ira_change_pseudo_allocno_class (int regno, r
 #undef TARGET_ASM_OUTPUT_SOURCE_FILENAME
 #define TARGET_ASM_OUTPUT_SOURCE_FILENAME mips_output_filename
 
+#undef TARGET_OUTPUT_CFI_ENDPROC
+#define TARGET_OUTPUT_CFI_ENDPROC mips_cfi_endproc
+
 #undef TARGET_SHIFT_TRUNCATION_MASK
 #define TARGET_SHIFT_TRUNCATION_MASK mips_shift_truncation_mask
 
Index: gcc/config/mips/mips.h
===================================================================
--- gcc/config/mips/mips.h	(revision 226409)
+++ gcc/config/mips/mips.h	(working copy)
@@ -592,6 +592,9 @@ struct mips_cpu_info {
 									\
       if (TARGET_CACHE_BUILTIN)						\
 	builtin_define ("__GCC_HAVE_BUILTIN_MIPS_CACHE");		\
+									\
+      if (TARGET_COMPACT_EH)						\
+	builtin_define ("__GNU_COMPACT_EH__");				\
     }									\
   while (0)
 
@@ -2823,11 +2826,12 @@ while (0)
 /* This is how to declare a function name.  The actual work of
    emitting the label is moved to function_prologue, so that we can
    get the line number correctly emitted before the .ent directive,
-   and after any .file directives.  Define as empty so that the function
-   is not declared before the .ent directive elsewhere.  */
+   and after any .file directives.  Define as mostly empty so that the
+   function is not declared before the .ent directive elsewhere.  */
 
 #undef ASM_DECLARE_FUNCTION_NAME
-#define ASM_DECLARE_FUNCTION_NAME(STREAM,NAME,DECL)
+#define ASM_DECLARE_FUNCTION_NAME(STREAM,NAME,DECL) \
+  mips_fixup_cfi_sections ();
 
 /* This is how to store into the string LABEL
    the symbol_ref name of an internal numbered label where
@@ -3149,10 +3153,42 @@ extern GTY(()) struct target_globals *micromips_gl
    versions of the linker know how to do this for indirect pointers,
    and for personality data.  We must fall back on using writable
    .eh_frame sections for shared libraries if the linker does not
-   support this feature.  */
+   support this feature.
+   
+   For compact EH frames we have a special relocation we can use.  */
+
+#define MIPS_COMPACT_EH_PCREL 1
+
+#define MIPS_COMPACT_EH_ENCODING (DW_EH_PE_pcrel | DW_EH_PE_sdata4)
+
 #define ASM_PREFERRED_EH_DATA_FORMAT(CODE,GLOBAL) \
-  (((GLOBAL) ? DW_EH_PE_indirect : 0) | DW_EH_PE_absptr)
+  (TARGET_COMPACT_EH \
+   ? MIPS_COMPACT_EH_ENCODING \
+   : (((GLOBAL) ? DW_EH_PE_indirect : 0) | DW_EH_PE_absptr))
 
+/* Handle special EH pointer encodings.  */
+
+#define ASM_MAYBE_OUTPUT_ENCODED_ADDR_RTX(FILE, ENCODING, SIZE, ADDR, DONE) \
+  do {									\
+    if ((SIZE) == 4 && (ENCODING) == MIPS_EH_ENCODING)			\
+      {									\
+        fputs ("\t.ehword\t", FILE);					\
+        assemble_name (FILE, XSTR (ADDR, 0));				\
+        goto DONE;							\
+      }									\
+  } while (0)
+
+#define MIPS_EH_ENCODING \
+  (DW_EH_PE_datarel | DW_EH_PE_sdata4 | DW_EH_PE_indirect)
+
+#define md_unwind_compact_opcode_finish 0x5c
+
+#define CRT_GET_RFIB_DATA(BASE)			\
+  {						\
+    extern char _gp[];				\
+    BASE = &_gp[0];				\
+  }
+
 /* For switching between MIPS16 and non-MIPS16 modes.  */
 #define SWITCHABLE_TARGET 1
 
Index: gcc/final.c
===================================================================
--- gcc/final.c	(revision 226409)
+++ gcc/final.c	(working copy)
@@ -4454,7 +4454,7 @@ rest_of_handle_final (void)
   /* The IA-64 ".handlerdata" directive must be issued before the ".endp"
      directive that closes the procedure descriptor.  Similarly, for x64 SEH.
      Otherwise it's not strictly necessary, but it doesn't hurt either.  */
-  output_function_exception_table (fnname);
+  output_function_exception_table (fnname, false);
 
   assemble_end_function (current_function_decl, fnname);
 
Index: gcc/configure.ac
===================================================================
--- gcc/configure.ac	(revision 226409)
+++ gcc/configure.ac	(working copy)
@@ -4966,6 +4966,13 @@ AC_DEFINE_UNQUOTED(LD_COMPRESS_DEBUG_OPTION, "$gcc
 [Define to the linker option to enable compressed debug sections.])
 AC_MSG_RESULT($gcc_cv_ld_compress_debug)
 
+gcc_GAS_CHECK_FEATURE([CFI .eh_frame_entry],
+ gcc_cv_as_eh_frame_entry,
+ ,,
+[	.cfi_sections .eh_frame_entry ],,
+[AC_DEFINE(HAVE_GAS_EH_FRAME_ENTRY, 1,
+[Define if your assembler supports generation of .eh_frame_entry from CFI directives.])])
+
 # --------
 # UNSORTED
 # --------
Index: gcc/target.def
===================================================================
--- gcc/target.def	(revision 226409)
+++ gcc/target.def	(working copy)
@@ -918,6 +918,12 @@ DEFHOOK
  tree, (const char *name),
  default_mangle_assembler_name)
 
+DEFHOOK
+(output_cfi_endproc,
+ "Emit the @code{.cfi_endproc} directive at the end of a function, and\
+ optionally additional exception handling information for it.",
+ void, (void), default_asm_output_cfi_endproc)
+
 HOOK_VECTOR_END (asm_out)
 
 /* Functions relating to instruction scheduling.  All of these
Index: gcc/defaults.h
===================================================================
--- gcc/defaults.h	(revision 226409)
+++ gcc/defaults.h	(working copy)
@@ -392,6 +392,10 @@ see the files COPYING3 and COPYING.RUNTIME respect
 #define MASK_RETURN_ADDR NULL_RTX
 #endif
 
+#ifndef TARGET_COMPACT_EH
+#define TARGET_COMPACT_EH 0
+#endif
+
 /* If we have named section and we support weak symbols, then use the
    .jcr section for recording java classes which need to be registered
    at program start-up time.  */
Index: gcc/except.c
===================================================================
--- gcc/except.c	(revision 226409)
+++ gcc/except.c	(working copy)
@@ -222,7 +222,6 @@ static void dw2_build_landing_pads (void);
 static int collect_one_action_chain (action_hash_type *, eh_region);
 static int add_call_site (rtx, int, int);
 
-static void push_uleb128 (vec<uchar, va_gc> **, unsigned int);
 static void push_sleb128 (vec<uchar, va_gc> **, int);
 #ifndef HAVE_AS_LEB128
 static int dw2_size_of_call_site_table (int);
@@ -230,7 +229,37 @@ static int sjlj_size_of_call_site_table (void);
 #endif
 static void dw2_output_call_site_table (int, int);
 static void sjlj_output_call_site_table (void);
+static const unsigned char *read_sleb128 (const unsigned char *p,
+                                          HOST_WIDE_INT *val);
 
+
+/* Emit the compact LSDA tables if using a compact personality
+   routine.  We avoid a language-specific check to cover the LTO
+   case where the language is "GNU GIMPLE".  */
+static bool
+using_compact_pr (void)
+{
+  rtx personality = get_personality_function (current_function_decl);
+
+  if (personality
+      && strncmp (XSTR (personality, 0), "__gnu_compact", 13) == 0)
+    return true;
+
+  return false;
+}
+
+/* Return the id to be used with .cfi_personality_id, or 0 to use
+   .cfi_personality.  */
+int
+compact_pr_id (rtx personality)
+{
+  if (!flag_asynchronous_unwind_tables
+      && personality
+      && strncmp (XSTR (personality, 0), "__gnu_compact_pr", 16) == 0)
+    return XSTR (personality, 0)[16] - '0';
+
+  return 0;
+}
 \f
 void
 init_eh (void)
@@ -2677,7 +2706,7 @@ make_pass_convert_to_eh_region_ranges (gcc::contex
   return new pass_convert_to_eh_region_ranges (ctxt);
 }
 \f
-static void
+void
 push_uleb128 (vec<uchar, va_gc> **data_area, unsigned int value)
 {
   do
@@ -2710,6 +2739,7 @@ push_sleb128 (vec<uchar, va_gc> **data_area, int v
   while (more);
 }
 
+
 \f
 #ifndef HAVE_AS_LEB128
 static int
@@ -2932,7 +2962,785 @@ output_ttype (tree type, int tt_format, int tt_for
     dw2_asm_output_encoded_addr_rtx (tt_format, value, is_public, NULL);
 }
 
+static const unsigned char *
+read_sleb128 (const unsigned char *p, HOST_WIDE_INT *val)
+{
+  unsigned int shift = 0;
+  unsigned char byte;
+  HOST_WIDE_INT result;
+
+  result = 0;
+  do
+    {
+      byte = *p++;
+      result |= ((HOST_WIDE_INT) byte & 0x7f) << shift;
+      shift += 7;
+    }
+  while (byte & 0x80);
+
+  /* Sign-extend a negative value.  */
+  if (shift < 8 * sizeof(result) && (byte & 0x40) != 0)
+    result |= -(((unsigned HOST_WIDE_INT)1L) << shift);
+
+  *val = (HOST_WIDE_INT) result;
+  return p;
+}
+
+static const char func_begin_lab[] = "LFB";
+static const char switch_begin_lab[] = "LFCB";
+static const char region_begin_lab[] = "LEHB";
+static const char region_end_lab[] = "LEHE";
+
+/* Structure to track the action chain for each region.  */
+struct GTY (()) chain_d
+{
+  int dwarf_regno;
+  int action_ndx;
+  struct chain_d *next;
+};
+
+typedef struct chain_d *chain_ref;
+static GTY (()) vec<chain_ref, va_gc> *chains;
+
+/* Structure used to store information about each region.  */
+struct GTY (()) compact_info_d
+{
+  /*  Region type.  */
+  enum eh_compact_header_type region_type;
+
+  /* Corresponding dwarf region number, if any.  */
+  int dwarf_region_no;
+
+  /* If not a dwarf region, the previous dwarf region number.  */
+  int prev_dwarf_region_no;
+
+  /* If not a dwarf region, the next dwarf region number.  */
+  int next_dwarf_region_no;
+
+  /* For action entries, the index into the action table.  */
+  int first_action_ndx;
+
+  /* For action entries, the next index into the action table.  */
+  int next_action_ndx;
+
+  /* The landing pad.  */
+  rtx landing_pad;
+
+  /* The entry in the action table, pointed to by the action index.  */
+  int action_value;
+
+  /* Non-zero, if this entry chains.  The chain-value is relative to
+     the current entry and excludes nothrow regions.  */
+  int chain_value;
+
+  /* True if this entry terminates a chain.  */
+  bool end_of_chain;
+
+  /* The label prefix to used to mark the beginning of this region.   */
+  const char *begin_lab_prefix;
+
+  /* The label prefix used to mark the end of this region.   */
+  const char *end_lab_prefix;
+
+  /* The label number denoting the beginning of the region.  */
+  int begin_lab_no;
+
+  /* The label number denoting the end of the region.  */
+  int end_lab_no;
+
+  /* True if the next item in the chain is a dummy action region.  */
+  bool use_dummy;
+
+  /* The region number of the dummy action.  */
+  int dummy_index;
+};
+
+typedef struct compact_info_d *compact_info_ref;
+static GTY (()) vec<compact_info_ref, va_gc> *compact_info;
+
+/* Struct used to track dummy action regions.  */
+
+struct GTY (()) dummy_info_d
+{
+  /* The dwarf region number that caused this dummy to be created.  */
+  int dwarf_region_no;
+
+  /* Index into the action table.  */
+  int action_ndx;
+
+  /* The next index into the action table.  */
+  int next_action_ndx;
+
+  /* The entry in the action table, pointed to by the action index.  */
+  int action_value;
+
+  /* Non-zero, if this entry chains.  The chain-value is relative to
+     the current entry and excludes nothrow regions.  */
+  int chain_value;
+
+  /* True if this entry terminates a chain.  */
+  bool end_of_chain;
+};
+
+typedef struct dummy_info_d *dummy_info_ref;
+static GTY (()) vec<dummy_info_ref, va_gc> *dummy_info;
+
+/* Create the region start label.  */
+
 static void
+compact_create_region_start_label (compact_info_d *ci, char *region_start)
+{
+  switch (ci->region_type)
+    {
+    case ECHT_DUMMY_ACTION_CHAIN:
+      break;
+
+    default:
+      ASM_GENERATE_INTERNAL_LABEL (region_start, ci->begin_lab_prefix,
+				   ci->begin_lab_no);
+    }
+}
+
+/* Create the region end label.  */
+
+static void
+compact_create_region_end_label (compact_info_d *ci, char *region_end)
+{
+  switch (ci->region_type)
+    {
+    case ECHT_DUMMY_ACTION_CHAIN:
+      break;
+
+    default:
+      ASM_GENERATE_INTERNAL_LABEL (region_end,
+				   ci->end_lab_prefix, ci->end_lab_no);
+      break;
+    }
+}
+
+/* Emit the region header for this entry.  */
+static void
+compact_emit_region_header (int regno, compact_info_d *ci)
+{
+  char region_start[32];
+  char region_end[32];
+  char landing_pad[32];
+
+  compact_create_region_start_label (ci, &region_start[0]);
+  compact_create_region_end_label (ci, &region_end[0]);
+
+  if (ci->landing_pad != NULL_RTX
+      && ci->landing_pad != constm1_rtx)
+    ASM_GENERATE_INTERNAL_LABEL (landing_pad, "L",
+                                 CODE_LABEL_NUMBER (ci->landing_pad));
+
+  switch (ci->region_type)
+    {
+    case ECHT_ACTION_CHAIN:
+      dw2_asm_output_comment ("Region %d -- Action Chain", regno);
+      break;
+
+    case ECHT_CLEANUP:
+      dw2_asm_output_comment ("Region %d -- Cleanup", regno);
+      break;
+
+    case ECHT_DUMMY_ACTION_CHAIN:
+      dw2_asm_output_comment ("Region %d -- Dummy Action Chain", regno);
+      break;
+	
+    case ECHT_CONTINUE_UNWINDING:
+      dw2_asm_output_comment ("Region %d -- Continue Unwinding", regno);
+      break;
+    case ECHT_NOTHROW:
+      dw2_asm_output_comment ("Region %d -- NoThrow", regno);
+      break;
+    }
+
+  if (ci->region_type == ECHT_DUMMY_ACTION_CHAIN)
+    dw2_asm_output_data_uleb128 (0, "Zero length region");
+
+  else if (ci->region_type != ECHT_NOTHROW)
+    dw2_asm_output_compact_region_length (region_end,
+					  region_start, false, "Length");
+  else
+    dw2_asm_output_compact_region_length (region_end,
+					  region_start, true, "Length");
+
+  if (ci->landing_pad == constm1_rtx)
+    dw2_asm_output_data_sleb128 (-1, "landing pad");
+  else if (ci->landing_pad != NULL_RTX)
+    dw2_asm_output_compact_landing_pad (landing_pad,
+					region_end, "Landing Pad Offset");
+}
+
+/* Build a region table entry for a dummy nothrow.  */
+
+static void
+compact_insert_nothrow (int section, int prev_dwarf_region_no)
+{
+  compact_info_d *ci = ggc_cleared_alloc<compact_info_d> ();
+
+  ci->region_type = ECHT_NOTHROW;
+  ci->prev_dwarf_region_no = prev_dwarf_region_no;
+  ci->next_dwarf_region_no = prev_dwarf_region_no + 1;
+  ci->end_of_chain = true;
+
+  /* The beginning label of a dummy nothrow region is either
+     the beginning of the function or the end label of the
+     previous dwarf region.  */
+  if (vec_safe_length (compact_info) == 0)
+    {
+      ci->begin_lab_prefix = section == 0 ? func_begin_lab : switch_begin_lab;
+      ci->begin_lab_no = current_function_funcdef_no;
+    }
+  else
+    {
+      ci->begin_lab_prefix = region_end_lab;
+      ci->begin_lab_no = call_site_base + prev_dwarf_region_no;
+    }
+
+  /* The end label of a dummy nothrow region is the
+     beginning of the next dwarf region.  */
+  ci->end_lab_prefix = region_begin_lab;
+  ci->end_lab_no = call_site_base + prev_dwarf_region_no + 1;
+
+  vec_safe_push (compact_info, ci);
+}
+
+/* Build a region table entry for a dwarf-based continue-unwind region.  */
+static void
+compact_insert_continue (int dwarf_regno)
+{
+  compact_info_d *ci = ggc_cleared_alloc<compact_info_d> ();
+
+  ci->region_type = ECHT_CONTINUE_UNWINDING;
+  ci->dwarf_region_no = dwarf_regno;
+  ci->landing_pad = constm1_rtx;
+  ci->prev_dwarf_region_no = dwarf_regno - 1;
+  ci->next_dwarf_region_no = dwarf_regno + 1;
+  ci->end_of_chain = true;
+  ci->begin_lab_prefix = region_begin_lab;
+  ci->end_lab_prefix = region_end_lab;
+  ci->begin_lab_no = call_site_base + dwarf_regno;
+  ci->end_lab_no = call_site_base + dwarf_regno;
+  vec_safe_push (compact_info, ci);
+}
+
+/* Build a region table entry for a dwarf-based cleanup region.  */
+static void
+compact_insert_cleanup (int dwarf_regno, rtx lp)
+{
+  compact_info_d *ci = ggc_cleared_alloc<compact_info_d> ();
+
+  ci->region_type = ECHT_CLEANUP;
+  ci->dwarf_region_no = dwarf_regno;
+  ci->landing_pad = lp;
+  ci->end_of_chain = true;
+  ci->begin_lab_prefix = region_begin_lab;
+  ci->end_lab_prefix = region_end_lab;
+  ci->begin_lab_no = call_site_base + dwarf_regno;
+  ci->end_lab_no = call_site_base + dwarf_regno;
+
+  vec_safe_push (compact_info, ci);
+}
+
+/* Set up the chain values for the dummies.  */
+
+static void
+compact_link_to_dummy (int dummy_index)
+{
+  int i;
+  compact_info_d *ci;
+  for (i = 0; vec_safe_iterate (compact_info, i, &ci); ++i)
+    {
+      if (ci->use_dummy
+          && ci->dummy_index == dummy_index)
+	ci->chain_value = vec_safe_length (compact_info) - i;
+    }
+}
+
+/* Emit the dummy action chain region entries.  */
+
+static void
+compact_emit_dummies (void)
+{
+  int i;
+  dummy_info_ref di;
+
+  
+  for (i = 0; vec_safe_iterate (dummy_info, i, &di); ++i)
+    {
+      compact_info_d *ci = ggc_cleared_alloc<compact_info_d> ();
+      compact_link_to_dummy (i);
+      ci->region_type = ECHT_DUMMY_ACTION_CHAIN;
+      ci->action_value = di->action_value;
+      ci->chain_value = di->chain_value;
+      vec_safe_push (compact_info, ci);
+    }
+}
+
+/* Given an offset into the dwarf action table, return the region
+   number of a dummy action record that matches it.  */
+
+static bool
+compact_find_dummy (int looking_for, int *goto_dummy)
+{
+  int i;
+  dummy_info_ref di;
+
+  for (i = 0; vec_safe_iterate (dummy_info, i, &di); ++i)
+    {
+      if (looking_for == di->action_ndx)
+	{
+	  *goto_dummy = i;
+	  return true;
+	}
+    }
+  return false;
+}
+
+/* Traverse the base chain and a chain entry until the 
+   chains match or the end of one or both chains is reached.  */
+
+static bool
+compact_chains_match (chain_d *base_ce, chain_d *goto_ce)
+{
+
+  /* No match if the action indices are different.  */
+  if (base_ce->action_ndx != goto_ce->action_ndx)
+    return false;
+
+  /* Match if we reach the end of both chains at the same time.  */
+  if (base_ce->next == NULL
+      && goto_ce->next == NULL)
+    return true;
+
+  /* No match if we reach the end of one entry.  */
+  if (base_ce->next == NULL
+      || goto_ce->next == NULL)
+    return false;
+
+  return compact_chains_match (base_ce->next, goto_ce->next);
+
+}
+
+/* Given a dwarf region (base), search the chain entries to
+   determine if an existing entry matches the base chain.  */ 
+
+static bool
+compact_find_chain (int dwarf_regno, int *goto_regno)
+{
+  int i;
+  chain_ref ce;
+  chain_ref base_ce = (*chains)[dwarf_regno];
+
+  for (i = 0; vec_safe_iterate (chains, i, &ce); ++i)
+    {
+      /* Don't match self.  */
+      if (base_ce->dwarf_regno == ce->dwarf_regno)
+	continue;
+
+      /* Don't match if the next action index of the base is different than this
+	 entry's initial action index. */
+      if (base_ce->next->action_ndx != ce->action_ndx)
+	continue;
+
+      /* Match subsequent chain entries.  */
+      if (compact_chains_match (base_ce->next, ce))
+	{
+	  *goto_regno = i;
+	  return true;
+	}
+    }
+  return false;
+}
+
+/* Insert a dummy action chain in the regions list.  */
+
+static void
+compact_insert_dummy (int filter, int index, bool first, compact_info_d *ci)
+{
+  dummy_info_d *di = ggc_cleared_alloc<dummy_info_d> ();
+
+  /* If this is not the first dummy in this chain, then
+     the last dummy entry chains here.  */
+  if (!first)
+    {
+      int i;
+      dummy_info_ref di_prev;
+      
+      i = dummy_info->length () - 1;
+      di_prev = (*dummy_info)[i];
+      di_prev->chain_value = 1;
+    }
+
+  di->action_value = filter;
+  di->action_ndx = index;
+  
+  vec_safe_push (dummy_info, di);
+
+  /* If this is the first dummy in this chain, record the
+     chain-to-this-dummy info in the main region.  */
+  if (first)
+    {
+      ci->use_dummy = true;
+      ci->dummy_index = dummy_info->length( ) - 1;
+    }
+}
+
+/* Return the number of nothrow or continue-unwind regions between
+   two region region entries.  */
+static int
+compact_count_intervening_nocount (int lower, int higher)
+{
+  int i;
+  int ignore = 0;
+
+  if (vec_safe_is_empty (compact_info))
+    return ignore;
+
+  for (i = lower; i <= higher; i++)
+    {
+      compact_info_ref ci = (*compact_info)[i];
+  
+      if (ci->region_type == ECHT_NOTHROW
+	  || ci->region_type == ECHT_CONTINUE_UNWINDING)
+	ignore++;
+    }
+
+  return ignore;
+}
+
+/* Walk the DWARF2 exception-handling tables and emit a compact-encoding
+   of the information partitioned like so:
+
+   1.  A uleb128 offset to the end of the region data
+   2.  The region data
+   3.  Exception specification tables, if present
+   4.  Alignment padding
+   5.  Array of type data
+*/
+
+#define USE_ACTION_EXTENSION 2
+
+static void
+output_one_function_compact_eh_table (int section)
+{
+  int i;
+  unsigned int k;
+  uchar uc;
+  char cs_begin[32];
+  char cs_end[32];
+  char ehspec_begin[32];
+  char ehspec_end[32];
+  bool empty_exception_spec = false;
+  compact_info_d *ci, *ci2;
+
+  unsigned int action_value;
+  int action_extension = 0;
+  int no_ehspecs, regno;
+  int tt_format = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/1);
+  int tt_format_size = size_of_encoded_value (tt_format);
+  const unsigned char *ar_start =
+    vec_safe_address (crtl->eh.action_record_data);
+  
+  int nregions = vec_safe_length ((&x_rtl)->eh.call_site_record_v[section]);
+
+  targetm.asm_out.internal_label (asm_out_file, section ? "LLSDAC" : "LLSDA",
+                                  current_function_funcdef_no);
+
+  ASM_GENERATE_INTERNAL_LABEL (cs_begin, section ? "LLSDACSBC" : "LLSDACSB",
+                               current_function_funcdef_no);
+
+  ASM_GENERATE_INTERNAL_LABEL (cs_end, section ? "LLSDACSEC" : "LLSDACSE",
+                               current_function_funcdef_no);
+
+  no_ehspecs = cfun->eh->ehspec_data.other->length ();
+
+  if (no_ehspecs > 0)
+    dw2_asm_output_data (1, 0x42, "PR 2, EHspecs present");
+  else
+    dw2_asm_output_data (1, 0x2, "PR 2");
+  
+  if (nregions == 0)
+    {
+      dw2_asm_output_data_uleb128 (0, "Call-site length");
+      return;
+    }
+  else
+    dw2_asm_output_delta_uleb128 (cs_end, cs_begin, "Call-site length");
+
+  assemble_name (asm_out_file, cs_begin);
+  fputs (":\n", asm_out_file);
+
+  /* Build an action chain for each dwarf region.  */
+  for (i = 0; i < nregions; i++)
+    {
+      const unsigned char *p;
+      HOST_WIDE_INT ar_filter, ar_disp;
+      struct call_site_record_d *cs =
+	(*(&x_rtl)->eh.call_site_record_v[section])[i];
+      chain_d *ce = ggc_cleared_alloc<chain_d> ();
+      ce->dwarf_regno = i;
+
+      if (cs->action == 0)
+	vec_safe_push (chains, ce);
+      else
+	{
+	  p = ar_start + cs->action -1;
+	  p = read_sleb128 (p, &ar_filter);
+	  p = read_sleb128 (p, &ar_disp);
+	  ce->action_ndx = cs->action;
+	  vec_safe_push (chains, ce);
+	
+	  while (ar_disp != 0)
+	    {
+	      chain_d *next_ce = ggc_cleared_alloc<chain_d> ();
+	      ce->next = next_ce;
+	      next_ce->dwarf_regno = i;
+
+	      p = ar_start + (p - ar_start) + ar_disp - 1;
+	      p = read_sleb128 (p, &ar_filter);
+	      p = read_sleb128 (p, &ar_disp);
+
+	      next_ce->action_ndx = (p - 1) - ar_start;
+	      ce = next_ce;
+	    }
+	}
+    }
+
+  /* Walk the regions again, this time building compact region entries.  */
+  for (i = 0, regno = 0; i < nregions; ++i)
+    {
+      /* i counts the dwarf regions.  regno counts compact regions.  */
+      const unsigned char *p;
+      HOST_WIDE_INT ar_filter, ar_disp;
+
+      const unsigned char *ar_start =
+	vec_safe_address (crtl->eh.action_record_data);
+
+      struct call_site_record_d *cs =
+	(*(&x_rtl)->eh.call_site_record_v[section])[i];
+
+      /* Create a dummy nothrow region prior to each dwarf region.  The
+	 dummy nothrow region spans the addresses between
+         the dwarf regions.  */
+      compact_insert_nothrow (section, i - 1);
+      regno++;
+
+      if (cs->action == 0 && cs->landing_pad == 0)
+	{
+	  compact_insert_continue (i);
+	}
+      else if (cs->action == 0)
+	{
+	  compact_insert_cleanup (i, cs->landing_pad);
+	}
+      else
+	{
+	  compact_info_d *ci = ggc_cleared_alloc<compact_info_d> ();
+
+	  p = vec_safe_address (crtl->eh.action_record_data)
+			   + cs->action - 1;
+
+	  p = read_sleb128 (p, &ar_filter);
+	  p = read_sleb128 (p, &ar_disp);
+
+	  
+	  if (ar_filter == 0)
+	    ci->region_type = ECHT_CLEANUP;
+	  else
+	    ci->region_type = ECHT_ACTION_CHAIN;
+
+	  ci->dwarf_region_no = i;
+	  ci->first_action_ndx = cs->action;
+	  ci->action_value = ar_filter;
+	  ci->next_action_ndx = cs->action + 1 + ar_disp;
+	  ci->landing_pad = cs->landing_pad;
+	  ci->begin_lab_prefix = region_begin_lab;
+	  ci->end_lab_prefix = region_end_lab;
+	  ci->begin_lab_no = call_site_base + ci->dwarf_region_no;
+	  ci->end_lab_no = call_site_base + ci->dwarf_region_no;
+	  vec_safe_push (compact_info, ci);
+
+	  /* A non-zero action record displacement denotes a chain.  If 
+	     the chain is already present in the compact region records,
+	     re-use the existing chain.  If not, dummy action region
+	     entries are created.  Re-use existing dummy action region
+	     entries if possible.  */
+
+	  if (ar_disp != 0)
+	    {
+	      int goto_regno, goto_dummy;
+
+	      if (compact_find_chain (ci->dwarf_region_no, &goto_regno))
+		ci->chain_value = goto_regno;
+	      else if (compact_find_dummy (ci->next_action_ndx, &goto_dummy))
+		{
+		  ci->use_dummy = true;
+		  ci->dummy_index = goto_dummy;
+		}
+	      else
+		{
+		  bool first_dummy = true;
+
+		  while (ar_disp != 0)
+		    {
+		      int ar_index;
+		      p = ar_start + (p - ar_start) + ar_disp - 1;
+		      ar_index = p - ar_start + 1;
+		      p = read_sleb128 (p, &ar_filter);
+		      p = read_sleb128 (p, &ar_disp);
+		      compact_insert_dummy (ar_filter, ar_index, first_dummy, ci);
+		      first_dummy = false;
+		    }
+		}
+	    }
+
+	  if (ar_disp == 0)
+	    ci->end_of_chain = true;
+	}
+    }
+
+
+  for (i = 0; vec_safe_iterate (compact_info, i, &ci); ++i)
+    {
+      int j;
+      if (ci->end_of_chain == true
+	  || ci->region_type == ECHT_DUMMY_ACTION_CHAIN)
+	continue;
+
+      for (j = 0; vec_safe_iterate (compact_info, j, &ci2); ++j)
+	{
+	  if (ci2->first_action_ndx == ci->next_action_ndx)
+	    {
+	      ci->chain_value = j - i;
+	      break;
+	    }
+	}     
+    }
+
+  /* The dummy action record entries are emitted last.  */
+  compact_emit_dummies ();
+
+  /* no-throw and continue-unwind regions are excluded in the chain values.  */
+  for (i = 0; vec_safe_iterate (compact_info, i, &ci); ++i)
+    {
+      int goto_regno;
+      int nocount = 0;
+      compact_info_ref ci_next = NULL;
+
+      if (i < (int) vec_safe_length (compact_info) - 1)
+	ci_next = (*compact_info)[i + 1];
+
+      if (ci->chain_value == 1
+	  && ci_next != NULL
+	  && ci_next->region_type == ECHT_DUMMY_ACTION_CHAIN)
+	continue;
+
+      if (ci->chain_value == 0)
+	continue;
+
+      goto_regno = i + ci->chain_value;
+      if (goto_regno > i)
+	{
+	  nocount = compact_count_intervening_nocount (i, goto_regno);
+	  ci->chain_value = ci->chain_value - nocount;
+	}
+      else
+	{
+	  nocount = compact_count_intervening_nocount (goto_regno, i);
+	  ci->chain_value = ci->chain_value + nocount;
+	}
+    }
+
+  /* Emit the region entries.  */
+
+  for (i = 0; vec_safe_iterate (compact_info, i, &ci); ++i)
+    {
+      compact_emit_region_header (i, ci);
+
+      if (ci->region_type == ECHT_ACTION_CHAIN
+	  || ci->region_type == ECHT_DUMMY_ACTION_CHAIN
+	  || ci->region_type == ECHT_CLEANUP)
+	{
+	  if (!IN_RANGE (ci->action_value, -1, 1))
+	    {
+	      action_value = USE_ACTION_EXTENSION;
+	      action_extension = ci->action_value;
+	    }
+	  else
+	    {
+	      if (ci->action_value == -1)
+		action_value = 3;
+	      else
+		action_value = ci->action_value;
+	    }
+	  dw2_asm_output_compact_ac_pair_sleb128 (action_value,
+						  ci->chain_value,
+						  "Action/Chain Pair");
+
+	  if (action_value == USE_ACTION_EXTENSION)
+	    dw2_asm_output_data_sleb128 (action_extension, "Action extension");
+	}
+
+    }
+  assemble_name (asm_out_file, cs_end);
+  fputs (":\n", asm_out_file);
+
+  /* Emit the exception specification indices.  */
+  if (no_ehspecs == 1)
+    { 
+      uchar ehspec = (*cfun->eh->ehspec_data.other)[0];
+      if (ehspec == 0)
+	empty_exception_spec = true;
+    }
+
+  if (no_ehspecs != 0)
+    {
+      if (empty_exception_spec)
+	{
+	  dw2_asm_output_data (1, 0x0, "Length of EH Specs");
+    	}
+      else
+	{
+	  ASM_GENERATE_INTERNAL_LABEL (ehspec_begin, section ? "LLSDATTDC" : "LLSDATTD",
+				       current_function_funcdef_no);
+
+	  ASM_GENERATE_INTERNAL_LABEL (ehspec_end, section ? "LLSDATTC" : "LLSDATT",
+				       current_function_funcdef_no);
+
+	  dw2_asm_output_delta_uleb128 (ehspec_end, ehspec_begin, "Length of EH Specs");
+
+	  assemble_name (asm_out_file, ehspec_begin);
+	  fputs (":\n", asm_out_file);
+
+	  k = no_ehspecs;
+	  for (k = 0;
+	       vec_safe_iterate (cfun->eh->ehspec_data.other, k, &uc); ++k)
+	    dw2_asm_output_data (1, uc, k ? NULL : "Exception specification table");
+
+	  assemble_name (asm_out_file, ehspec_end);
+	  fputs (":\n", asm_out_file);
+        }
+    }
+
+  fputs (".align 2\n", asm_out_file);
+
+  /* Emit the type table entries.  */
+  for (k = 0; k < cfun->eh->ttype_data->length (); k++)
+    {
+      tree type = (*cfun->eh->ttype_data)[k];
+      output_ttype (type, tt_format, tt_format_size);
+    }
+
+  call_site_base += nregions;
+  vec_free (chains);
+  vec_free (dummy_info);
+  vec_free (compact_info);
+}
+
+static void
 output_one_function_exception_table (int section)
 {
   int tt_format, cs_format, lp_format, i;
@@ -3109,7 +3917,7 @@ output_one_function_exception_table (int section)
 }
 
 void
-output_function_exception_table (const char *fnname)
+output_function_exception_table (const char *fnname, bool after_switch_section)
 {
   rtx personality = get_personality_function (current_function_decl);
 
@@ -3117,6 +3925,9 @@ void
   if (! crtl->uses_eh_lsda)
     return;
 
+  if (after_switch_section && !using_compact_pr ())
+    return;
+
   if (personality)
     {
       assemble_external_libcall (personality);
@@ -3130,9 +3941,19 @@ void
   /* If the target wants a label to begin the table, emit it here.  */
   targetm.asm_out.emit_except_table_label (asm_out_file);
 
-  output_one_function_exception_table (0);
-  if (crtl->eh.call_site_record_v[1])
-    output_one_function_exception_table (1);
+  if (using_compact_pr ())
+    {
+      if (after_switch_section || crtl->eh.call_site_record_v[1] == NULL)
+	output_one_function_compact_eh_table (0);
+      else
+	output_one_function_compact_eh_table (1);
+    }
+  else
+    {
+      output_one_function_exception_table (0);
+      if (crtl->eh.call_site_record_v[1])
+	output_one_function_exception_table (1);
+    }
 
   switch_to_section (current_function_section ());
 }
Index: gcc/except.h
===================================================================
--- gcc/except.h	(revision 226409)
+++ gcc/except.h	(working copy)
@@ -62,6 +62,15 @@ enum eh_region_type
   ERT_MUST_NOT_THROW
 };
 
+/* Region header types in the compact EH scheme.  */
+enum eh_compact_header_type
+{
+  ECHT_ACTION_CHAIN,
+  ECHT_CLEANUP,
+  ECHT_CONTINUE_UNWINDING,
+  ECHT_DUMMY_ACTION_CHAIN,
+  ECHT_NOTHROW
+};
 
 /* A landing pad for a given exception region.  Any transfer of control
    from the EH runtime to the function happens at a landing pad.  */
@@ -224,12 +233,14 @@ extern void for_each_eh_label (void (*) (rtx));
 
 extern void init_eh_for_function (void);
 
+extern int compact_pr_id (rtx);
+
 extern void remove_eh_landing_pad (eh_landing_pad);
 extern void remove_eh_handler (eh_region);
 extern void remove_unreachable_eh_regions (sbitmap);
 
 extern bool current_function_has_exception_handlers (void);
-extern void output_function_exception_table (const char *);
+extern void output_function_exception_table (const char *, bool);
 
 extern rtx expand_builtin_eh_pointer (tree);
 extern rtx expand_builtin_eh_filter (tree);
@@ -284,6 +295,8 @@ extern eh_landing_pad get_eh_landing_pad_from_rtx
 
 extern void finish_eh_generation (void);
 
+extern void push_uleb128 (vec<uchar, va_gc> **, unsigned int);
+
 struct GTY(()) throw_stmt_node {
   gimple stmt;
   int lp_nr;
Index: gcc/dwarf2out.c
===================================================================
--- gcc/dwarf2out.c	(revision 226409)
+++ gcc/dwarf2out.c	(working copy)
@@ -288,6 +288,10 @@ static GTY(()) rtx current_unit_personality;
 #define FUNC_BEGIN_LABEL	"LFB"
 #endif
 
+#ifndef FUNC_BEGIN_SWITCH_LABEL
+#define FUNC_BEGIN_SWITCH_LABEL	"LFCB"
+#endif
+
 #ifndef FUNC_END_LABEL
 #define FUNC_END_LABEL		"LFE"
 #endif
@@ -904,7 +908,8 @@ output_call_frame_info (int for_eh)
       dw2_asm_output_data_uleb128 (augmentation_size, "Augmentation size");
       if (personality)
 	{
-	  dw2_asm_output_data (1, per_encoding, "Personality (%s)",
+	  dw2_asm_output_data (1, per_encoding & ~GCC_DW_EH_PE_special,
+			       "Personality (%s)",
 			       eh_data_format_name (per_encoding));
 	  dw2_asm_output_encoded_addr_rtx (per_encoding,
 					   personality,
@@ -912,11 +917,13 @@ output_call_frame_info (int for_eh)
 	}
 
       if (any_lsda_needed)
-	dw2_asm_output_data (1, lsda_encoding, "LSDA Encoding (%s)",
+	dw2_asm_output_data (1, lsda_encoding & ~GCC_DW_EH_PE_special,
+			     "LSDA Encoding (%s)",
 			     eh_data_format_name (lsda_encoding));
 
       if (fde_encoding != DW_EH_PE_absptr)
-	dw2_asm_output_data (1, fde_encoding, "FDE Encoding (%s)",
+	dw2_asm_output_data (1, fde_encoding & ~GCC_DW_EH_PE_special,
+			     "FDE Encoding (%s)",
 			     eh_data_format_name (fde_encoding));
     }
 
@@ -957,11 +964,15 @@ dwarf2out_do_cfi_startproc (bool second)
 {
   int enc;
   rtx ref;
-  rtx personality = get_personality_function (current_function_decl);
+  rtx personality;
 
   fprintf (asm_out_file, "\t.cfi_startproc\n");
 
-  if (personality)
+  personality = get_personality_function (current_function_decl);
+  enc = compact_pr_id (personality);
+  if (enc != 0)
+    fprintf (asm_out_file, "\t.cfi_personality_id 0x%x\n", enc);
+  else if (personality)
     {
       enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2, /*global=*/1);
       ref = personality;
@@ -973,7 +984,8 @@ dwarf2out_do_cfi_startproc (bool second)
       if (enc & DW_EH_PE_indirect)
 	ref = dw2_force_const_mem (ref, true);
 
-      fprintf (asm_out_file, "\t.cfi_personality %#x,", enc);
+      fprintf (asm_out_file, "\t.cfi_personality %#x,",
+	       enc & ~GCC_DW_EH_PE_special);
       output_addr_const (asm_out_file, ref);
       fputc ('\n', asm_out_file);
     }
@@ -991,7 +1003,8 @@ dwarf2out_do_cfi_startproc (bool second)
       if (enc & DW_EH_PE_indirect)
 	ref = dw2_force_const_mem (ref, true);
 
-      fprintf (asm_out_file, "\t.cfi_lsda %#x,", enc);
+      fprintf (asm_out_file, "\t.cfi_lsda %#x,",
+	       enc & ~GCC_DW_EH_PE_special);
       output_addr_const (asm_out_file, ref);
       fputc ('\n', asm_out_file);
     }
@@ -1154,7 +1167,7 @@ dwarf2out_end_epilogue (unsigned int line ATTRIBUT
   cached_next_real_insn = NULL;
 
   if (dwarf2out_do_cfi_asm ())
-    fprintf (asm_out_file, "\t.cfi_endproc\n");
+    targetm.asm_out.output_cfi_endproc ();
 
   /* Output a label to mark the endpoint of the code generated for this
      function.  */
@@ -1222,8 +1235,10 @@ dwarf2out_switch_text_section (void)
     dwarf2out_note_section_used ();
 
   if (dwarf2out_do_cfi_asm ())
-    fprintf (asm_out_file, "\t.cfi_endproc\n");
+    targetm.asm_out.output_cfi_endproc ();
 
+  output_function_exception_table (NULL, true);
+
   /* Now do the real section switch.  */
   sect = current_function_section ();
   switch_to_section (sect);
@@ -1232,6 +1247,9 @@ dwarf2out_switch_text_section (void)
     = (sect == text_section
        || (cold_text_section && sect == cold_text_section));
 
+  ASM_OUTPUT_DEBUG_LABEL (asm_out_file, FUNC_BEGIN_SWITCH_LABEL,
+			  current_function_funcdef_no);
+
   if (dwarf2out_do_cfi_asm ())
     dwarf2out_do_cfi_startproc (true);
 
Index: gcc/expr.c
===================================================================
--- gcc/expr.c	(revision 226409)
+++ gcc/expr.c	(working copy)
@@ -11241,7 +11241,6 @@ const_vector_from_tree (tree exp)
 tree
 build_personality_function (const char *lang)
 {
-  const char *unwind_and_version;
   tree decl, type;
   char *name;
 
@@ -11250,21 +11249,23 @@ build_personality_function (const char *lang)
     case UI_NONE:
       return NULL;
     case UI_SJLJ:
-      unwind_and_version = "_sj0";
+      name = ACONCAT (("__", lang, "_personality_sj0", NULL));
       break;
     case UI_DWARF2:
     case UI_TARGET:
-      unwind_and_version = "_v0";
+      if (TARGET_COMPACT_EH
+	  && (strcmp (lang_hooks.name, "GNU C++") == 0))
+	name = ACONCAT (("__gnu_compact_pr2", NULL));
+      else
+	name = ACONCAT (("__", lang, "_personality_v0", NULL));
       break;
     case UI_SEH:
-      unwind_and_version = "_seh0";
+      name = ACONCAT (("__", lang, "_personality_seh0", NULL));
       break;
     default:
       gcc_unreachable ();
     }
 
-  name = ACONCAT (("__", lang, "_personality", unwind_and_version, NULL));
-
   type = build_function_type_list (integer_type_node, integer_type_node,
 				   long_long_unsigned_type_node,
 				   ptr_type_node, ptr_type_node, NULL_TREE);
Index: gcc/configure
===================================================================
--- gcc/configure	(revision 226409)
+++ gcc/configure	(working copy)
@@ -27574,6 +27574,37 @@ _ACEOF
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_ld_compress_debug" >&5
 $as_echo "$gcc_cv_ld_compress_debug" >&6; }
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for CFI .eh_frame_entry" >&5
+$as_echo_n "checking assembler for CFI .eh_frame_entry... " >&6; }
+if test "${gcc_cv_as_eh_frame_entry+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  gcc_cv_as_eh_frame_entry=no
+  if test x$gcc_cv_as != x; then
+    $as_echo '	.cfi_sections .eh_frame_entry ' > conftest.s
+    if { ac_try='$gcc_cv_as $gcc_cv_as_flags  -o conftest.o conftest.s >&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+    then
+	gcc_cv_as_eh_frame_entry=yes
+    else
+      echo "configure: failed program was" >&5
+      cat conftest.s >&5
+    fi
+    rm -f conftest.o conftest.s
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_as_eh_frame_entry" >&5
+$as_echo "$gcc_cv_as_eh_frame_entry" >&6; }
+if test $gcc_cv_as_eh_frame_entry = yes; then
+
+$as_echo "#define HAVE_GAS_EH_FRAME_ENTRY 1" >>confdefs.h
+
+fi
+
 # --------
 # UNSORTED
 # --------
Index: gcc/dwarf2cfi.c
===================================================================
--- gcc/dwarf2cfi.c	(revision 226409)
+++ gcc/dwarf2cfi.c	(working copy)
@@ -3425,10 +3425,12 @@ dwarf2out_do_cfi_asm (void)
   /* Make sure the personality encoding is one the assembler can support.
      In particular, aligned addresses can't be handled.  */
   enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2,/*global=*/1);
-  if ((enc & 0x70) != 0 && (enc & 0x70) != DW_EH_PE_pcrel)
+  if ((enc & 0x70) != 0 && (enc & 0x70) != DW_EH_PE_pcrel
+      && (enc & GCC_DW_EH_PE_special) == 0)
     return false;
   enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0,/*global=*/0);
-  if ((enc & 0x70) != 0 && (enc & 0x70) != DW_EH_PE_pcrel)
+  if ((enc & 0x70) != 0 && (enc & 0x70) != DW_EH_PE_pcrel
+      && (enc & GCC_DW_EH_PE_special) == 0)
     return false;
 
   /* If we can't get the assembler to emit only .debug_frame, and we don't need
Index: gcc/testsuite/g++.dg/eh/catch6.C
===================================================================
--- gcc/testsuite/g++.dg/eh/catch6.C	(revision 0)
+++ gcc/testsuite/g++.dg/eh/catch6.C	(revision 0)
@@ -0,0 +1,53 @@
+using namespace std;
+
+extern "C" void abort ();
+
+class except1 {
+public:
+    explicit except1 (int _i) {}
+};
+
+class except2 {
+public:
+    explicit except2 (int _i) {}
+};
+
+void
+bar (int i)
+{
+  if (i & 1)
+    throw except1 (i);
+  throw except2 (i);
+}
+
+void
+f1 (int i)
+{
+  try { bar (i); }
+  catch (const except1& ex) { ; }
+}
+
+void
+f2 (int i)
+{
+  try { f1 (i); }
+  catch (const except2& ex) { ;; }
+}
+
+void
+foo (int i)
+{
+  try { f2 (i); }
+  catch (...)
+    {
+      abort ();
+    }
+}
+
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 50; i++)
+    foo (i);
+}
Index: libstdc++-v3/libsupc++/Makefile.in
===================================================================
--- libstdc++-v3/libsupc++/Makefile.in	(revision 226409)
+++ libstdc++-v3/libsupc++/Makefile.in	(working copy)
@@ -118,9 +118,10 @@ am__objects_1 = array_type_info.lo atexit_arm.lo a
 	bad_alloc.lo bad_array_length.lo bad_array_new.lo bad_cast.lo \
 	bad_typeid.lo class_type_info.lo del_op.lo del_ops.lo \
 	del_opnt.lo del_opv.lo del_opvs.lo del_opvnt.lo dyncast.lo \
-	eh_alloc.lo eh_arm.lo eh_aux_runtime.lo eh_call.lo eh_catch.lo \
-	eh_exception.lo eh_globals.lo eh_personality.lo eh_ptr.lo \
-	eh_term_handler.lo eh_terminate.lo eh_tm.lo eh_throw.lo \
+	eh_alloc.lo eh_arm.lo eh_aux_runtime.lo eh_call.lo \
+	eh_catch.lo eh_compact_pr.lo \
+	eh_exception.lo eh_globals.lo eh_personality.lo \
+	eh_ptr.lo eh_term_handler.lo eh_terminate.lo eh_tm.lo eh_throw.lo \
 	eh_type.lo eh_unex_handler.lo enum_type_info.lo \
 	function_type_info.lo fundamental_type_info.lo guard.lo \
 	guard_error.lo hash_bytes.lo nested_exception.lo \
@@ -423,6 +424,7 @@ sources = \
 	eh_aux_runtime.cc \
 	eh_call.cc \
 	eh_catch.cc \
+	eh_compact_pr.cc \
 	eh_exception.cc \
 	eh_globals.cc \
 	eh_personality.cc \
Index: libstdc++-v3/libsupc++/eh_compact_pr.cc
===================================================================
--- libstdc++-v3/libsupc++/eh_compact_pr.cc	(revision 0)
+++ libstdc++-v3/libsupc++/eh_compact_pr.cc	(revision 0)
@@ -0,0 +1,682 @@
+#ifdef __GNU_COMPACT_EH__
+// -*- C++ -*- The GNU C++ compact exception personality routine
+// Copyright (C) 2012
+// 
+// Free Software Foundation, Inc.
+//
+// This file is part of GCC.
+//
+// GCC is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// GCC is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+//
+// Written by Catherine Moore <clm@codesourcery.com>
+
+#include <bits/c++config.h>
+#include <cstdlib>
+#include <bits/exception_defines.h>
+#include <cxxabi.h>
+#include "unwind-cxx.h"
+
+using namespace __cxxabiv1;
+
+#include "unwind-pe.h"
+
+\f
+enum exception_entry_type
+{
+  CATCH_TYPE,
+  CLEANUP_TYPE,
+  CONTINUE_UNWINDING_TYPE,
+  EH_SPEC_TYPE,
+  NOTHROW_TYPE,
+  UNKNOWN_TYPE,
+}; 
+
+#define LENGTH_MASK 1
+
+#define NOTHROW_REGION(x) (x & 0x1)
+#define CONTINUE_UNWINDING_REGION(x) (x == -1)
+#define DUMMY_ACTION_REGION(x) (x == 0)
+#define EXTRACT_ACTION_VALUE(x) (((x & 3) ^ 2) - 2)
+#define EXTRACT_CHAIN_VALUE(x) (x >> 2)
+#define ACTION_EXTENSION -2
+#define HAVE_EHSPECS(x) (x & 0x40)
+#define TTABLE_START(x) (x + 4 - 1) & ~(4 - 1);
+
+// Return an element from a type table.
+
+static const std::type_info *
+compact_get_ttype_entry (_Unwind_Ptr base,
+			 unsigned char eh_encoding,
+			 _Unwind_Ptr ttab_start,
+			 int aval)
+{
+  _Unwind_Ptr ptr;
+
+  aval = (aval -  1) * size_of_encoded_value (eh_encoding);
+  read_encoded_value_with_base (eh_encoding, base,
+				(const unsigned char *) ttab_start + aval, &ptr);
+
+  return reinterpret_cast<const std::type_info *>(ptr);
+}
+
+// Given the thrown type THROW_TYPE, pointer to a variable containing a
+// pointer to the exception object THROWN_PTR_P and a type CATCH_TYPE to
+// compare against, return whether or not there is a match and if so,
+// update *THROWN_PTR_P.
+
+static bool
+get_adjusted_ptr (const std::type_info *catch_type,
+		  const std::type_info *throw_type,
+		  void **thrown_ptr_p)
+{
+  void *thrown_ptr = *thrown_ptr_p;
+
+  // Pointer types need to adjust the actual pointer, not
+  // the pointer to pointer that is the exception object.
+  // This also has the effect of passing pointer types
+  // "by value" through the __cxa_begin_catch return value.
+  if (throw_type->__is_pointer_p ())
+    thrown_ptr = *(void **) thrown_ptr;
+
+  if (catch_type->__do_catch (throw_type, &thrown_ptr, 1))
+    {
+      *thrown_ptr_p = thrown_ptr;
+      return true;
+    }
+
+  return false;
+}
+
+// Save stage1 handler information in the exception object.
+
+static inline void
+save_caught_exception (struct _Unwind_Exception *ue_header,
+		       void *thrown_ptr,
+		       int handler_switch_value,
+		       const unsigned char *language_specific_data,
+		       _Unwind_Ptr landing_pad)
+{
+  __cxa_exception* xh = __get_exception_header_from_ue(ue_header);
+
+  xh->handlerSwitchValue = handler_switch_value;
+  xh->languageSpecificData = language_specific_data;
+  xh->adjustedPtr = thrown_ptr;
+  xh->catchTemp = landing_pad;
+}
+
+// Restore the catch handler information saved during phase1.
+
+static inline void
+restore_caught_exception (struct _Unwind_Exception *ue_header,
+			  int &handler_switch_value,
+			  const unsigned char *& language_specific_data,
+			  _Unwind_Ptr& landing_pad)
+{
+  __cxa_exception* xh = __get_exception_header_from_ue(ue_header);
+  handler_switch_value = xh->handlerSwitchValue;
+  language_specific_data = xh->languageSpecificData;
+  landing_pad = (_Unwind_Ptr) xh->catchTemp;
+}
+
+// PCS_PTR points to a variable holding a pointer to the start of a new
+// LSDA entry.  This function parses the entry and updates PCS_PTR to
+// point past its end.  The variables at *PREGION_LEN, *PLP_OFF, *PAV and
+// *PCV are filled with information about the data found: the region
+// length, the landing pad offset, the action value and the chain value.
+
+static inline enum exception_entry_type
+compact_parse_header (const unsigned char **pcs_ptr, _uleb128_t *pregion_len,
+		      int *pav, int *pcv, _sleb128_t *plp_off)
+{
+  const unsigned char *cs_ptr = *pcs_ptr;
+  _uleb128_t region_len;
+  enum exception_entry_type type = NOTHROW_TYPE;
+
+  cs_ptr = read_uleb128 (cs_ptr, pregion_len);
+
+  region_len = *pregion_len;
+
+  /* If nothrow, advance to next entry.  */
+  if (!NOTHROW_REGION (region_len))
+    {
+      _sleb128_t lp = 0;
+      /* Dummy action entries, don't have landing pads.  */
+      if (!DUMMY_ACTION_REGION (region_len))
+	{
+	  cs_ptr = read_sleb128 (cs_ptr, plp_off);
+	  lp = *plp_off;
+	}
+      /* If continue_unwinding, advance to next entry.  */
+      if (CONTINUE_UNWINDING_REGION (lp))
+	type = CONTINUE_UNWINDING_TYPE;
+      else
+	{
+	  int action_value, chain_value;
+	  _sleb128_t ac_pair;
+
+	  cs_ptr = read_sleb128 (cs_ptr, &ac_pair);
+	  action_value = EXTRACT_ACTION_VALUE (ac_pair);
+	  chain_value = EXTRACT_CHAIN_VALUE (ac_pair);
+	  if (action_value == ACTION_EXTENSION)
+	    {
+	      _sleb128_t ac_ext;
+	      cs_ptr = read_sleb128 (cs_ptr, &ac_ext);
+	      action_value = ac_ext;
+	    }
+
+	  if (action_value == 0)
+	    type = CLEANUP_TYPE;
+	  else if (action_value > 0)
+	    type = CATCH_TYPE;
+	  else
+	    type = EH_SPEC_TYPE;
+
+	  *pav = action_value;
+	  *pcv = chain_value;
+	}
+    }
+
+  *pcs_ptr = cs_ptr;
+  return type;
+}
+
+/* Given a region entry number, return a pointer to its call-site
+   table entry.  */
+
+static const unsigned char *
+compact_goto_region (const unsigned char *cs_ptr, int region_no,
+		     int chain_to_region)
+{
+  while (true)
+    {
+      _uleb128_t region_len;
+      int action_value, chain_value;
+      _sleb128_t lp_off;
+
+      region_no++;
+      if (region_no == chain_to_region)
+	break;
+
+      compact_parse_header (&cs_ptr, &region_len, &action_value,
+			    &chain_value, &lp_off);
+    }
+  return cs_ptr;
+}
+
+/* Return the next region number.  The next region is the current
+   region number less the chain_value.  No Throw regions are ignored.  */
+
+static int
+compact_backward_find_chain (const unsigned char *cs_start,
+			     int region_no, int chain_value)
+{
+  int looking_for = region_no - 1;
+  int looking_at = -1;
+  const unsigned char *cs_ptr = cs_start;
+
+  while (true)
+    {
+      _uleb128_t region_len;
+      int action_value, unused_chain_value;
+      _sleb128_t lp_off;
+      enum exception_entry_type type;
+
+      looking_at++;
+      type = compact_parse_header (&cs_ptr, &region_len, &action_value,
+				   &unused_chain_value, &lp_off);
+
+      if (looking_at == looking_for)
+	{
+	  if (type != NOTHROW_TYPE && type != CONTINUE_UNWINDING_TYPE)
+	    chain_value = chain_value + 1;
+
+	  if (chain_value == 0)
+	    break;
+
+	  looking_at = -1;
+	  looking_for--;
+	  cs_ptr = cs_start;
+	}
+    }
+  return looking_at;
+}
+
+/* Return the next region number.  chain_value is the number of
+   region entries in the call-site table to advance.  No Throw
+   regions are ignored and not included in the count.  */
+
+static int
+compact_forward_find_chain (const unsigned char *cs_ptr, int region_no,
+			    int chain_value)
+{
+  int valid_regions = 0;
+
+  while (true)
+    {
+      _uleb128_t region_len;
+      int action_value, unused_chain_value;
+      _sleb128_t lp_off;
+      enum exception_entry_type type;
+
+      region_no++;
+      type = compact_parse_header (&cs_ptr, &region_len, &action_value,
+				   &unused_chain_value, &lp_off);
+
+      if (type == NOTHROW_TYPE || type == CONTINUE_UNWINDING_TYPE)
+	continue;
+
+      valid_regions++;
+      if (valid_regions == chain_value)
+	return region_no;
+    }
+}
+
+/* Return the next region number in the chain.  */
+
+static int
+compact_find_chain_to_region (const unsigned char *cs_start,
+			      const unsigned char **cs_ptr,
+			      int *region_no, int chain_value)
+{
+  int chain_to_region;
+
+  if (chain_value < 0)
+    chain_to_region
+      = compact_backward_find_chain (cs_start, *region_no, chain_value);
+  else
+    chain_to_region
+      = compact_forward_find_chain (*cs_ptr, *region_no, chain_value);
+
+  return chain_to_region;
+}
+
+typedef const std::type_info _throw_typet;
+
+namespace __cxxabiv1
+{
+
+#pragma GCC visibility push(default)
+extern "C" _Unwind_Reason_Code
+__gnu_compact_pr2 (int version __attribute__ ((unused)),
+		   _Unwind_Action actions,
+		   _Unwind_Exception_Class exception_class,
+		   struct _Unwind_Exception *ue_header,
+		   struct _Unwind_Context *context)
+{
+  const unsigned char *cs_ptr, *language_specific_data;
+  const unsigned char *cs_start, *cs_end;
+  const unsigned char *padding_start;
+  const unsigned char *ehspecs_start;
+
+  _Unwind_Ptr ip, ip_start, ip_end;
+  _Unwind_Ptr lp_start, landing_pad;
+  _Unwind_Ptr ttable_start;
+  _Unwind_Ptr lsda;
+
+  _sleb128_t lp_off;
+  _uleb128_t region_len;
+  _uleb128_t call_site_len;
+  _uleb128_t ehspecs_len;
+
+  const std::type_info* catch_type;
+  int chain_value, action_value;
+  int region_no;
+  int handler_switch_value;
+  bool chaining = false;
+  int chain_to_region;
+  enum exception_entry_type region_type;
+  bool foreign_exception;
+  int ip_before_insn = 0;
+
+  void *thrown_ptr = 0;
+  _throw_typet *throw_type;
+  unsigned char eh_encoding = _Unwind_GetEhEncoding (context);
+  _Unwind_Ptr base = base_of_encoded_value (eh_encoding, context);
+  __cxa_exception *xh = __get_exception_header_from_ue(ue_header);
+
+  enum found_handler_type
+  {
+    found_nothing,
+    found_terminate,
+    found_cleanup,
+    found_handler,
+  } found_type = found_nothing;
+
+  language_specific_data = (const unsigned char *)
+    _Unwind_GetLanguageSpecificData (context);
+
+  // If no LSDA, then there are no handlers or cleanups.
+  if (!language_specific_data)
+    return _URC_CONTINUE_UNWIND;
+
+  /* Read the LSDA header.  */
+  cs_ptr =
+    read_encoded_value (0, DW_EH_PE_udata1, language_specific_data, &lsda);
+  /* Then the Call-site length.  */
+  cs_ptr = read_uleb128 (cs_ptr, &call_site_len);
+  cs_start = cs_ptr;
+  cs_end = cs_start + call_site_len;
+
+  /* Read the ehspecs if they are present and setup
+     a pointer to the type table entries.  */
+  if (HAVE_EHSPECS (lsda))
+    {
+      ehspecs_start = read_uleb128 (cs_start + call_site_len, &ehspecs_len);
+      padding_start = ehspecs_start + ehspecs_len;
+      ttable_start = TTABLE_START ((_Unwind_Ptr) padding_start);
+    }
+  else
+    ttable_start = TTABLE_START ((_Unwind_Ptr) cs_start + call_site_len);
+
+  foreign_exception = !__is_gxx_exception_class(exception_class);
+  // Shortcut for phase 2 found handler for domestic exception.
+  if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
+      && !foreign_exception)
+    {
+      restore_caught_exception (ue_header, handler_switch_value,
+				language_specific_data, landing_pad);
+      found_type = (landing_pad == 0 ? found_terminate : found_handler);
+      goto install_context;
+    }
+
+#ifdef _GLIBCXX_HAVE_GETIPINFO
+  ip = _Unwind_GetIPInfo (context, &ip_before_insn);
+#else
+  ip = _Unwind_GetIP (context);
+#endif
+  if (!ip_before_insn)
+    --ip;
+
+  ip_start = lp_start = _Unwind_GetRegionStart (context);
+  region_no = 0;
+
+  /* Examine each region entry, looking for
+     a matching ip address or chain value.  */
+  while (cs_ptr < cs_end)
+    {
+      region_type = compact_parse_header (&cs_ptr, &region_len, &action_value,
+					  &chain_value, &lp_off);
+
+      if (DUMMY_ACTION_REGION (region_len) && !chaining)
+	break;
+
+      region_len &= ~LENGTH_MASK;
+      lp_start += region_len;
+      ip_end = ip_start + region_len;
+
+      switch (region_type)
+	{
+	case NOTHROW_TYPE:
+	  break;
+	
+	case CONTINUE_UNWINDING_TYPE:
+	  if (ip >= ip_start && ip <= ip_end)
+	    return _URC_CONTINUE_UNWIND;
+	  break;
+
+	case CLEANUP_TYPE:
+	  if (chaining
+	      || (ip >= ip_start && ip <= ip_end))
+	    {
+	      found_type = found_cleanup;
+	      handler_switch_value = action_value;
+	      if (!chaining)
+	        landing_pad = lp_start + lp_off;
+
+	      /* For cleanups, walk the chain.  The cleanup is
+		 processed only if there are no other matches.  */
+
+	      if (chain_value == 0)
+		goto found_something;
+	      else
+		{
+		  chaining = true;
+		  chain_to_region = 
+		      compact_find_chain_to_region (cs_start, &cs_ptr,
+						    &region_no, chain_value);
+		}
+	    }
+	  break;
+
+	case CATCH_TYPE:
+	  if (!chaining
+	      && (ip < ip_start || ip > ip_end))
+	    break;
+
+#ifdef __GXX_RTTI
+	  // During forced unwinding, match a magic exception type.
+	  if (actions & _UA_FORCE_UNWIND)
+	    throw_type = &typeid(abi::__forced_unwind);
+
+	  // With a foreign exception class, there's no exception type.
+	  else if (foreign_exception)
+	    throw_type = &typeid(abi::__foreign_exception);
+	  else
+#endif
+	    {
+	      thrown_ptr = __get_object_from_ue (ue_header);
+	      throw_type = __get_exception_header_from_obj
+		(thrown_ptr)->exceptionType;
+	    }
+	  catch_type =
+	    compact_get_ttype_entry (base,
+				     eh_encoding, ttable_start, action_value);
+
+	  if (catch_type == 0
+	      || get_adjusted_ptr (catch_type, throw_type, &thrown_ptr))
+	    {
+	      found_type = found_handler;
+	      handler_switch_value = action_value;
+	      if (!chaining)
+		landing_pad = lp_start + lp_off;
+	      goto found_something;
+	    }
+
+	  /* Walk the chain if we didn't match.  */
+	  if (chain_value != 0)
+	    {
+	      if (!chaining)
+		landing_pad = lp_start + lp_off;
+	      chaining = true;
+	      chain_to_region = 
+		compact_find_chain_to_region (cs_start, &cs_ptr,
+					      &region_no, chain_value);
+	    }
+	  else
+	    // IP matched and not chaining.
+	    goto found_something;
+
+	  break;
+
+	case EH_SPEC_TYPE:
+#ifdef __GXX_RTTI
+	  // During forced unwinding, match a magic exception type.
+	  if (actions & _UA_FORCE_UNWIND)
+	    throw_type = &typeid(abi::__forced_unwind);
+
+	  // With a foreign exception class, there's no exception type.
+	  else if (foreign_exception)
+	    throw_type = &typeid(abi::__foreign_exception);
+
+	  else
+#endif
+	    {
+	      thrown_ptr = __get_object_from_ue (ue_header);
+	      throw_type = __get_exception_header_from_obj
+		(thrown_ptr)->exceptionType;
+	    }
+	  if (ip >= ip_start && ip <= ip_end)
+	    {
+	      if (!chaining)
+		landing_pad = lp_start + lp_off;
+
+	      /* An ehspec of zero is an immediate match.  */
+	      if (ehspecs_len == 0)
+		{
+		  found_type = found_handler;
+		  handler_switch_value = action_value;
+		  goto found_something;
+		}
+	      else if (throw_type
+		       && !(actions & _UA_FORCE_UNWIND)
+		       && !foreign_exception)
+		{
+		  const unsigned char *p;
+		  _uleb128_t ehval;
+
+		  bool matched_spec = false;
+		  int si = abs (action_value) - 1;
+		  p = read_uleb128 (ehspecs_start + si, &ehval);
+		  /* Read the ehspec entries until a match is found or the
+		     list is exhausted.  Process as a handler if no match
+		     is achieved.  */
+		  while (!matched_spec && ehval != 0)
+		    {
+	              catch_type = compact_get_ttype_entry (base, eh_encoding,
+							    ttable_start, ehval);
+
+		      if (get_adjusted_ptr (catch_type, throw_type, &thrown_ptr))
+			matched_spec = true;
+		      p = read_uleb128 (p, &ehval);
+		    }
+		  if (!matched_spec)
+		    {
+		      found_type = found_handler;
+		      handler_switch_value = action_value;
+		      goto found_something;
+		    }
+		}
+	      if (chain_value != 0)
+		{
+		  if (!chaining)
+		    landing_pad = lp_start + lp_off;
+		  chaining = true;
+		  chain_to_region = 
+		    compact_find_chain_to_region (cs_start, &cs_ptr,
+						  &region_no, chain_value);
+		}
+	      else
+		// IP matched and not chaining.
+		goto found_something;
+	    }
+	  break;
+
+	case UNKNOWN_TYPE:
+	  break;
+	}
+
+      /* If a chain was discovered, find the next region entry in the chain.  */
+      if (chaining && region_no != chain_to_region)
+	{
+	  if (chain_to_region < region_no)
+	    cs_ptr = compact_goto_region (cs_start, -1, chain_to_region);
+	  else
+	    cs_ptr = compact_goto_region (cs_ptr, region_no, chain_to_region);
+	  region_no = chain_to_region - 1;
+	}
+
+      ip_start = ip_end;
+      region_no++;
+    }
+	    
+  /* Each region has been examined without finding a match.  */
+  if (found_type == found_nothing)
+    {
+      found_type = found_terminate;
+      landing_pad = 0;
+    }
+
+found_something:
+
+  if (found_type == found_nothing)
+    return _URC_CONTINUE_UNWIND;
+
+  if (actions & _UA_SEARCH_PHASE)
+    {
+      if (found_type == found_cleanup)
+	return _URC_CONTINUE_UNWIND;
+
+      if (!foreign_exception)
+        {
+          save_caught_exception(ue_header, thrown_ptr, handler_switch_value,
+				language_specific_data, landing_pad);
+        }
+      return _URC_HANDLER_FOUND;
+    }
+
+install_context:
+  
+  // We can't use any of the cxa routines with foreign exceptions,
+  // because they all expect ue_header to be a struct __cxa_exception.
+  // So in that case, call terminate or unexpected directly.
+  if ((actions & _UA_FORCE_UNWIND)
+      || foreign_exception)
+    {
+      if (found_type == found_terminate)
+	std::terminate ();
+      else if (handler_switch_value < 0)
+	{
+	  __try 
+	    { std::unexpected (); } 
+	  __catch(...) 
+	    { std::terminate (); }
+	}
+    }
+  else
+    {
+      if (found_type == found_terminate)
+	__cxa_call_terminate(ue_header);
+
+      // Cache the base value for __cxa_call_unexpected, as we won't
+      // have an _Unwind_Context then.
+      if (handler_switch_value < 0)
+	{
+	  xh->catchTemp = base_of_encoded_value (eh_encoding, context);
+	  /* For the compact encoding, bits 0-7 of
+	     xh->handler_switch_value are used to store the
+	     eh_encoding.  Bits 8-31 of xh_handler_switch_value
+	     are used to store the offset from the beginning of
+             the LSDA to the type count for this unmatched exception
+             specification.  We store this as a positive number to 
+	     signal to __cxa_call_unexpected that the LSDA is in 
+             the compact format.  */
+
+	  xh->handlerSwitchValue = abs (xh->handlerSwitchValue) << 8;;
+	  xh->handlerSwitchValue = xh->handlerSwitchValue | eh_encoding;
+	  handler_switch_value = -1;
+
+	}  
+    }
+
+  /* For targets with pointers smaller than the word size, we must
+     extend the pointer, and this extension is target dependent.  */
+  _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
+		 __builtin_extend_pointer (ue_header));
+  /* handler_switch_value will always be -1 for an
+     unmatched exception spec header.  */
+  _Unwind_SetGR (context, __builtin_eh_return_data_regno (1),
+		 handler_switch_value);
+  _Unwind_SetIP (context, landing_pad);
+  return _URC_INSTALL_CONTEXT;
+}
+
+#pragma GCC visibility pop
+} // namespace __cxxabiv1
+#endif
Index: libstdc++-v3/libsupc++/unwind-cxx.h
===================================================================
--- libstdc++-v3/libsupc++/unwind-cxx.h	(revision 226409)
+++ libstdc++-v3/libsupc++/unwind-cxx.h	(working copy)
@@ -363,6 +363,11 @@ extern "C" _Unwind_Reason_Code __gxx_personality_v
      (int, _Unwind_Action, _Unwind_Exception_Class,
       struct _Unwind_Exception *, struct _Unwind_Context *);
 
+// GNU C++ compact eh personality routine.
+extern "C" _Unwind_Reason_Code __gnu_compact_pr2
+     (int, _Unwind_Action, _Unwind_Exception_Class,
+      struct _Unwind_Exception *, struct _Unwind_Context *);
+
 // GNU C++ sjlj personality routine, Version 0.
 extern "C" _Unwind_Reason_Code __gxx_personality_sj0
      (int, _Unwind_Action, _Unwind_Exception_Class,
Index: libstdc++-v3/libsupc++/eh_personality.cc
===================================================================
--- libstdc++-v3/libsupc++/eh_personality.cc	(revision 226409)
+++ libstdc++-v3/libsupc++/eh_personality.cc	(working copy)
@@ -234,6 +234,63 @@ get_adjusted_ptr (const std::type_info *catch_type
   return false;
 }
 
+// Return an element from a type table.
+
+static const std::type_info *
+compact_get_ttype_entry (_Unwind_Ptr base,
+			 unsigned char eh_encoding,
+			 _Unwind_Ptr ttab_start,
+			 int aval)
+{
+  _Unwind_Ptr ptr;
+
+  aval = (aval -  1) * size_of_encoded_value (eh_encoding);
+  read_encoded_value_with_base (eh_encoding, base,
+				(const unsigned char *) ttab_start + aval,
+				&ptr);
+
+  return reinterpret_cast<const std::type_info *>(ptr);
+}
+
+// Return true if THROW_TYPE matches one of the exception spec entries.
+static bool
+check_compact_exception_spec (_throw_typet* throw_type, void* thrown_ptr,
+			      const unsigned char *xh_lsda,
+			      _Unwind_Sword xh_switch_value,
+			      _Unwind_Ptr base)
+
+{
+  _Unwind_Ptr padding_start, ttable_start;
+  const std::type_info* catch_type;
+  unsigned char encoding = xh_switch_value & 0xff;
+  unsigned char eh_offset = abs (xh_switch_value >> 8);
+  const unsigned char *p, *cs_start, *cs_end, *ehtable_start;
+  _Unwind_Ptr lsda_header;
+  _uleb128_t ehval, call_site_len, ehspecs_len;
+  
+
+  p = read_encoded_value (0, DW_EH_PE_udata1, xh_lsda, &lsda_header);
+  cs_start = read_uleb128 (p, &call_site_len);
+  cs_end = cs_start + call_site_len;
+  ehtable_start  = read_uleb128 (cs_end, &ehspecs_len);
+  padding_start = (_Unwind_Ptr) ehtable_start + ehspecs_len;
+  ttable_start = (padding_start + 4 - 1) & ~(4 - 1);
+
+  p = read_uleb128 (ehtable_start + eh_offset - 1, &ehval);
+
+  while (ehval != 0)
+    {
+      catch_type =
+	compact_get_ttype_entry (base, encoding, ttable_start, ehval);
+
+      if (get_adjusted_ptr (catch_type, throw_type, &thrown_ptr))
+	return true;
+
+      p = read_uleb128 (p, &ehval);
+    }
+  return false;
+}
+
 // Return true if THROW_TYPE matches one if the filter types.
 
 static bool
@@ -765,21 +822,38 @@ __cxa_call_unexpected (void *exc_obj_in)
       __cxa_exception *new_xh = globals->caughtExceptions;
       void *new_ptr = __get_object_from_ambiguous_exception (new_xh);
 
-      // We don't quite have enough stuff cached; re-parse the LSDA.
-      parse_lsda_header (0, xh_lsda, &info);
+      if (xh_switch_value > 0)
+	{
+	  _throw_typet *new_type = __get_exception_header_from_obj
+	    (new_ptr)->exceptionType;
+	  if (check_compact_exception_spec (new_type, new_ptr, xh_lsda,
+					    xh_switch_value, info.ttype_base))
+	    __throw_exception_again;
+	}
+      else
+	{
+	  // We don't quite have enough stuff cached; re-parse the LSDA.
+	  parse_lsda_header (0, xh_lsda, &info);
 
-      // If this new exception meets the exception spec, allow it.
-      if (check_exception_spec (&info, __get_exception_header_from_obj
-                                  (new_ptr)->exceptionType,
-				new_ptr, xh_switch_value))
-	{ __throw_exception_again; }
+	  // If this new exception meets the exception spec, allow it.
+	  if (check_exception_spec (&info, __get_exception_header_from_obj
+				    (new_ptr)->exceptionType, new_ptr,
+				    xh_switch_value))
+	     __throw_exception_again;
 
+	}
       // If the exception spec allows std::bad_exception, throw that.
       // We don't have a thrown object to compare against, but since
       // bad_exception doesn't have virtual bases, that's OK; just pass 0.
 #if __cpp_exceptions && __cpp_rtti
       const std::type_info &bad_exc = typeid (std::bad_exception);
-      if (check_exception_spec (&info, &bad_exc, 0, xh_switch_value))
+      if (xh_switch_value > 0)
+	{
+	  if (check_compact_exception_spec (&bad_exc, 0, xh_lsda,
+					    xh_switch_value, info.ttype_base))
+	    throw std::bad_exception();
+	}
+      else if (check_exception_spec (&info, &bad_exc, 0, xh_switch_value))
 	throw std::bad_exception();
 #endif   
 
Index: libstdc++-v3/libsupc++/Makefile.am
===================================================================
--- libstdc++-v3/libsupc++/Makefile.am	(revision 226409)
+++ libstdc++-v3/libsupc++/Makefile.am	(working copy)
@@ -66,6 +66,7 @@ sources = \
 	eh_aux_runtime.cc \
 	eh_call.cc \
 	eh_catch.cc \
+	eh_compact_pr.cc \
 	eh_exception.cc \
 	eh_globals.cc \
 	eh_personality.cc \
Index: libstdc++-v3/config/abi/pre/gnu.ver
===================================================================
--- libstdc++-v3/config/abi/pre/gnu.ver	(revision 226409)
+++ libstdc++-v3/config/abi/pre/gnu.ver	(working copy)
@@ -1909,6 +1909,7 @@ CXXABI_1.3 {
     __gxx_personality_v0;
     __gxx_personality_sj0;
     __gxx_personality_seh0;
+    __gnu_compact_pr2;
     __dynamic_cast;
 
     # *_type_info classes, ctor and dtor
Index: libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
===================================================================
--- libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver	(revision 226409)
+++ libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver	(working copy)
@@ -200,6 +200,7 @@ CXXABI_2.0 {
     __cxa_vec_new;
     __gxx_personality_v0;
     __gxx_personality_sj0;
+    __gnu_compact_pr2;
     __dynamic_cast;
 
     # std::exception_ptr

^ permalink raw reply	[flat|nested] 14+ messages in thread

* RE: [RFA] Compact EH  Patch
  2015-07-30 21:07 [RFA] Compact EH Patch Moore, Catherine
@ 2015-08-14 19:58 ` Moore, Catherine
  2015-09-09 20:45 ` Jason Merrill
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 14+ messages in thread
From: Moore, Catherine @ 2015-08-14 19:58 UTC (permalink / raw)
  To: gcc-patches; +Cc: jason, Matthew Fortune

Hi Jason,
Are you going to be able to review this patch sometime soon?
Thanks,
Catherine

> -----Original Message-----
> From: gcc-patches-owner@gcc.gnu.org [mailto:gcc-patches-
> owner@gcc.gnu.org] On Behalf Of Moore, Catherine
> Sent: Thursday, July 30, 2015 4:15 PM
> To: gcc-patches@gcc.gnu.org
> Cc: jason@redhat.com; Matthew Fortune
> Subject: [RFA] Compact EH Patch
> 
> This patch implements a more compact format for exception handling data.
> Although I don't have recent numbers for the amount of compression
> achieved, an earlier measurement showed a 30% reduction in the size of EH
> data for libstdc++.
> 
> A design document detailing the new format is available
> (https://github.com/MentorEmbedded/cxx-
> abi/blob/master/MIPSCompactEH.pdf).
> 
> This implementation enables the new format for MIPS targets only, but the
> generic pieces to enable the new format for other architectures is in place.
> I couldn't really think of a good way to split up the patch to make the review
> easier, but I am open to suggestions.
> I've successfully bootstrapped with the patch and completed testing across
> many of the MIPS targets and multilibs.
> I also ran a test series for the x86 without regressions.
> Thanks,
> Catherine
> 
> 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFA] Compact EH Patch
  2015-07-30 21:07 [RFA] Compact EH Patch Moore, Catherine
  2015-08-14 19:58 ` Moore, Catherine
@ 2015-09-09 20:45 ` Jason Merrill
  2015-09-09 23:53   ` Richard Henderson
  2015-09-18 19:34 ` Richard Henderson
  2015-10-28 16:44 ` Matthew Fortune
  3 siblings, 1 reply; 14+ messages in thread
From: Jason Merrill @ 2015-09-09 20:45 UTC (permalink / raw)
  To: Moore, Catherine, gcc-patches
  Cc: Matthew Fortune, Richard Henderson, Ian Lance Taylor

On 07/30/2015 04:14 PM, Moore, Catherine wrote:
> This patch implements a more compact format for exception handling data.  Although I don't have recent numbers for the amount of compression achieved, an earlier measurement showed a 30% reduction in the size of EH data for libstdc++.
>
> A design document detailing the new format is available (https://github.com/MentorEmbedded/cxx-abi/blob/master/MIPSCompactEH.pdf).
>
> This implementation enables the new format for MIPS targets only, but the generic pieces to enable the new format for other architectures is in place.

Hi, sorry for the slow response.

I'm surprised that there was no mention of this design on the ABI list, 
especially since you've decided to post the design document to its git 
repository.

I'm skeptical about the explicit rejection of asynchronous backtracing; 
this is an important capability for debug traces on hosted systems, 
which is why the compiler flag is on by default in many linux 
distributions.  The document mentions using libunwind instead, but that 
wouldn't help, as libunwind relies on the same unwind information.  So 
it seems to me that the objective in 1.2 of supporting both unhosted and 
Linux-hosted programs isn't sufficiently met.

Jason

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFA] Compact EH Patch
  2015-09-09 20:45 ` Jason Merrill
@ 2015-09-09 23:53   ` Richard Henderson
  2015-09-14 19:32     ` Moore, Catherine
  0 siblings, 1 reply; 14+ messages in thread
From: Richard Henderson @ 2015-09-09 23:53 UTC (permalink / raw)
  To: Jason Merrill, Moore, Catherine, gcc-patches
  Cc: Matthew Fortune, Ian Lance Taylor

On 09/09/2015 01:35 PM, Jason Merrill wrote:
> On 07/30/2015 04:14 PM, Moore, Catherine wrote:
>> This patch implements a more compact format for exception handling data.
>> Although I don't have recent numbers for the amount of compression achieved,
>> an earlier measurement showed a 30% reduction in the size of EH data for
>> libstdc++.
>>
>> A design document detailing the new format is available
>> (https://github.com/MentorEmbedded/cxx-abi/blob/master/MIPSCompactEH.pdf).
>>
>> This implementation enables the new format for MIPS targets only, but the
>> generic pieces to enable the new format for other architectures is in place.
>
> Hi, sorry for the slow response.
>
> I'm surprised that there was no mention of this design on the ABI list,
> especially since you've decided to post the design document to its git repository.
>
> I'm skeptical about the explicit rejection of asynchronous backtracing; this is
> an important capability for debug traces on hosted systems, which is why the
> compiler flag is on by default in many linux distributions.  The document
> mentions using libunwind instead, but that wouldn't help, as libunwind relies
> on the same unwind information.  So it seems to me that the objective in 1.2 of
> supporting both unhosted and Linux-hosted programs isn't sufficiently met.

Indeed.  Though that is certainly fixable.

Let us suppose for the moment that the note on page 17 becomes true -- use some 
of the currently unused encoding space for push/pop of state, and advancing the 
pc, so that one can represent asynchronous data.

At that point what's present there in section 10.1 looks plausible for use on 
MIPS.  With appropriate scheduling barriers in the mips prologue, it would in 
all likelyhood only add a single byte to the unwind info.

For instance, suppose

   0101 1011          akin to DW_CFA_remember_state
   0101 1111          akin to DW_CFA_restore_state
   0111 0000 uleb128  akin to DW_CFA_advance_loc, of uleb128 * CALIGN
   0111 xxxx          akin to DW_CFA_advance_loc, of xxxx * CALIGN

where CALIGN is 4 for mips32 and 2 for mips16/micromips.  This allows one to 
advance 15 insns with 1 byte, and 127 insns with 2 bytes.

For the first example in 10.2.1 is instructive, in that it would take 5 bytes 
to encode: pc += 1*4, sp += 56, pc += 8*4, push {16-22,31}, finish.  Given that 
most functions that allocate a stack frame will do so in the first insn, and 
indeed cannot do anything useful in zero instructions, one could make that 
first pc adjustment implicit, reducing the size of the unwind to 4 bytes, which 
does fit into your inline unwind info.

Anyway, the exact encodings of this are something for the mips maintainers, 
since it isn't applicable generically.

Of more interest to me is the rest of the proposal, particularly section 10.4. 
  I like that there's more locality to the unwind data than the current 
.gcc_except_table contents.  I like that there's less pointer chasing.

Looking at the contents of my desktop, the vast majority of binaries have no 
.gcc_except_table, or a trivially small amount.  But I do have 102 binaries 
with a table larger than 64k, with a maximum size of 705k.  So I also like the 
potential size savings of 25-40%.

The spec in section 10.4 looks good.  I can't see any issues with it off-hand.

The spec in section 8.2 out to be extended to handle 64-bit offsets instead of 
32-bit offsets, even if only by reserving version 3 for the purpose.  While 
MIPS may want to restrict the size of the elf object to 2GB, and that's the 
common case for most files on all systems, we cannot restrict the size on all 
systems.

Anyway, that's having read the referenced document only, and nothing yet of the 
code.  I'll try to get to that tomorrow.


r~

^ permalink raw reply	[flat|nested] 14+ messages in thread

* RE: [RFA] Compact EH Patch
  2015-09-09 23:53   ` Richard Henderson
@ 2015-09-14 19:32     ` Moore, Catherine
  0 siblings, 0 replies; 14+ messages in thread
From: Moore, Catherine @ 2015-09-14 19:32 UTC (permalink / raw)
  To: Richard Henderson, Jason Merrill, gcc-patches
  Cc: Matthew Fortune, Ian Lance Taylor, Sidwell, Nathan



> -----Original Message-----
> From: Richard Henderson [mailto:rth@redhat.com]
> Sent: Wednesday, September 09, 2015 7:46 PM
> To: Jason Merrill; Moore, Catherine; gcc-patches@gcc.gnu.org
> Cc: Matthew Fortune; Ian Lance Taylor
> Subject: Re: [RFA] Compact EH Patch
> 
> On 09/09/2015 01:35 PM, Jason Merrill wrote:
> > On 07/30/2015 04:14 PM, Moore, Catherine wrote:
> >> This patch implements a more compact format for exception handling
> data.
> >> Although I don't have recent numbers for the amount of compression
> >> achieved, an earlier measurement showed a 30% reduction in the size
> >> of EH data for
> >> libstdc++.
> >>
> >> A design document detailing the new format is available
> >> (https://github.com/MentorEmbedded/cxx-
> abi/blob/master/MIPSCompactEH.pdf).
> >>
> >> This implementation enables the new format for MIPS targets only, but
> >> the generic pieces to enable the new format for other architectures is in
> place.
> >
> > Hi, sorry for the slow response.
> >
> > I'm surprised that there was no mention of this design on the ABI
> > list, especially since you've decided to post the design document to its git
> repository.
> >
> > I'm skeptical about the explicit rejection of asynchronous
> > backtracing; this is an important capability for debug traces on
> > hosted systems, which is why the compiler flag is on by default in
> > many linux distributions.  The document mentions using libunwind
> > instead, but that wouldn't help, as libunwind relies on the same
> > unwind information.  So it seems to me that the objective in 1.2 of
> supporting both unhosted and Linux-hosted programs isn't sufficiently met.

The support of asynchronous unwinding was not a requirement for our customer and wasn't addressed in the design.
As Richard points out, it is certainly possible to extend the scheme to support it, although I don't think such support should be a requirement for acceptance of the patch.

> 
> Indeed.  Though that is certainly fixable.
> 
> Let us suppose for the moment that the note on page 17 becomes true --
> use some of the currently unused encoding space for push/pop of state, and
> advancing the pc, so that one can represent asynchronous data.
> 
> At that point what's present there in section 10.1 looks plausible for use on
> MIPS.  With appropriate scheduling barriers in the mips prologue, it would in
> all likelyhood only add a single byte to the unwind info.
> 
> For instance, suppose
> 
>    0101 1011          akin to DW_CFA_remember_state
>    0101 1111          akin to DW_CFA_restore_state
>    0111 0000 uleb128  akin to DW_CFA_advance_loc, of uleb128 * CALIGN
>    0111 xxxx          akin to DW_CFA_advance_loc, of xxxx * CALIGN
> 
> where CALIGN is 4 for mips32 and 2 for mips16/micromips.  This allows one to
> advance 15 insns with 1 byte, and 127 insns with 2 bytes.
> 
> For the first example in 10.2.1 is instructive, in that it would take 5 bytes to
> encode: pc += 1*4, sp += 56, pc += 8*4, push {16-22,31}, finish.  Given that
> most functions that allocate a stack frame will do so in the first insn, and
> indeed cannot do anything useful in zero instructions, one could make that
> first pc adjustment implicit, reducing the size of the unwind to 4 bytes, which
> does fit into your inline unwind info.
> 
> Anyway, the exact encodings of this are something for the mips maintainers,
> since it isn't applicable generically.
> 
> Of more interest to me is the rest of the proposal, particularly section 10.4.
>   I like that there's more locality to the unwind data than the current
> .gcc_except_table contents.  I like that there's less pointer chasing.
> 
> Looking at the contents of my desktop, the vast majority of binaries have no
> .gcc_except_table, or a trivially small amount.  But I do have 102 binaries with
> a table larger than 64k, with a maximum size of 705k.  So I also like the
> potential size savings of 25-40%.
> 
> The spec in section 10.4 looks good.  I can't see any issues with it off-hand.
> 
> The spec in section 8.2 out to be extended to handle 64-bit offsets instead of
> 32-bit offsets, even if only by reserving version 3 for the purpose.  While
> MIPS may want to restrict the size of the elf object to 2GB, and that's the
> common case for most files on all systems, we cannot restrict the size on all
> systems.
> 
> Anyway, that's having read the referenced document only, and nothing yet
> of the code.  I'll try to get to that tomorrow.
> 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFA] Compact EH Patch
  2015-07-30 21:07 [RFA] Compact EH Patch Moore, Catherine
  2015-08-14 19:58 ` Moore, Catherine
  2015-09-09 20:45 ` Jason Merrill
@ 2015-09-18 19:34 ` Richard Henderson
  2015-10-05 23:14   ` Moore, Catherine
                     ` (2 more replies)
  2015-10-28 16:44 ` Matthew Fortune
  3 siblings, 3 replies; 14+ messages in thread
From: Richard Henderson @ 2015-09-18 19:34 UTC (permalink / raw)
  To: Moore, Catherine, gcc-patches; +Cc: jason, Matthew Fortune

> Index: libgcc/libgcc-std.ver.in
> ===================================================================
> --- libgcc/libgcc-std.ver.in	(revision 226409)
> +++ libgcc/libgcc-std.ver.in	(working copy)
> @@ -1918,6 +1918,7 @@ GCC_4.6.0 {
>    __morestack_current_segment
>    __morestack_initial_sp
>    __splitstack_find
> +  _Unwind_GetEhEncoding
>  }
>  
>  %inherit GCC_4.7.0 GCC_4.6.0
> @@ -1938,3 +1939,8 @@ GCC_4.7.0 {
>  %inherit GCC_4.8.0 GCC_4.7.0
>  GCC_4.8.0 {
>  }
> +
> +%inherit GCC_4.8.0 GCC_4.7.0
> +GCC_4.8.0 {
> +  __register_frame_info_header_bases
> +}

You can't push new symbols into old versions.  These have to go into the
version for the current gcc.

> Index: libstdc++-v3/config/abi/pre/gnu.ver
> ===================================================================
> --- libstdc++-v3/config/abi/pre/gnu.ver	(revision 226409)
> +++ libstdc++-v3/config/abi/pre/gnu.ver	(working copy)
> @@ -1909,6 +1909,7 @@ CXXABI_1.3 {
>      __gxx_personality_v0;
>      __gxx_personality_sj0;
>      __gxx_personality_seh0;
> +    __gnu_compact_pr2;
>      __dynamic_cast;
>  
>      # *_type_info classes, ctor and dtor
> Index: libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
> ===================================================================
> --- libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver	(revision 226409)
> +++ libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver	(working copy)
> @@ -200,6 +200,7 @@ CXXABI_2.0 {
>      __cxa_vec_new;
>      __gxx_personality_v0;
>      __gxx_personality_sj0;
> +    __gnu_compact_pr2;
>      __dynamic_cast;
>  
>      # std::exception_ptr

Likewise.

> +  if (data.type != CET_not_found)
> +    return data.type;
> +
> +  return CET_not_found;

Return data.type unconditionally.

> +++ libgcc/unwind-pe.h	(working copy)
> @@ -44,6 +44,7 @@
>  #define DW_EH_PE_udata2         0x02
>  #define DW_EH_PE_udata4         0x03
>  #define DW_EH_PE_udata8         0x04
> +#define DW_EH_PE_udata1         0x05
>  #define DW_EH_PE_sleb128        0x09
>  #define DW_EH_PE_sdata2         0x0A
>  #define DW_EH_PE_sdata4         0x0B

If we're going to add udata1, we might as well add sdata1 for consistency.

> @@ -184,6 +187,7 @@ read_encoded_value_with_base (unsigned char encodi
>    union unaligned
>      {
>        void *ptr;
> +      unsigned u1 __attribute__ ((mode (QI)));
>        unsigned u2 __attribute__ ((mode (HI)));
>        unsigned u4 __attribute__ ((mode (SI)));
>        unsigned u8 __attribute__ ((mode (DI)));

This is silly.  Access to a single byte never requires alignment help from the
compiler...

> +	case DW_EH_PE_udata1:
> +	  result = u->u1;
> +	  p += 1;
> +	  break;

    result = *p;

> +  read_encoded_value_with_base (DW_EH_PE_absptr | DW_EH_PE_udata4, 0,
> +				hdr + 4, &nrec);

Err... that encoding type makes no sense: absptr is "pointer size".  Combining
that with an explicit size, like udata4, means that udata4 wins.  So this is
the same as simply writing DW_EH_PE_udata4.

At which point you're loading a 4 byte unsigned quantity from an aligned
address.  You might as well use *(uint32_t *)(hdr + 4).

> +__register_frame_info_header_bases (const void *begin, struct object *ob,
> +				    void *tbase, void *dbase)
> +{
> +  /* Only register compact EH frame headers.  */
> +  if (begin == NULL || *(const char *) begin != 2)
> +    return;

Check for no entries before registering?

> +extern char __GNU_EH_FRAME_HDR[] TARGET_ATTRIBUTE_WEAK;

Don't you have some guaranteed alignment for this table?
That perhaps ought to be seen in either the type or an attribute.

+      if (op < 0x40)
+      else if (op < 0x48)
+      else if (op < 0x50)
+      else if (op < 0x58)
+      else if (op == 0x58)
+      else if (op == 0x59)
+      else if (op == 0x5a)
+      else if (op == 0x5b)
+      else if (op == 0x5c)
+      else if (op == 0x5d)
+      else if (op == 0x5e)
+      else if (op == 0x5f)
+      else if (op >= 0x60 && op < 0x6c)

Better as a switch statement surely, using the gcc case x ... y: extension.

> +static _Unwind_Reason_Code
> +uw_frame_state_compact (struct _Unwind_Context *context,
> +			_Unwind_FrameState *fs,
> +			enum compact_entry_type entry_type,
> +		       	struct compact_eh_bases *bases)
> +{
> +  const unsigned char *p;
> +  unsigned int pr_index;
> +  _Unwind_Ptr personality;
> +  unsigned char buf[4];
> +  _Unwind_Reason_Code rc;
> +
> +  p = bases->entry;
> +  pr_index = *(p++);
> +  switch (pr_index) {
> +  case 0:
> +      p = read_encoded_value (context, bases->eh_encoding, p, &personality);
> +      fs->personality = (_Unwind_Personality_Fn) personality;
> +      break;
> +  case 1:
> +      fs->personality = __gnu_compact_pr1;
> +      break;
> +  case 2:
> +      fs->personality = __gnu_compact_pr2;
> +      break;
> +  default:
> +      fs->personality = NULL;
> +  }

This is the aspect of this spec about which I am least keen.  The existing
method whereby the personality function is explicit in each object file means
that we've got automatic version control on data that is private to the object
file.

That is, if we should ever change the format of the LSDA -- as in fact you are
doing here -- then all we need do is change __gcc_personality_v0 to
__gcc_personality_v1, and all is well.  One can mix and match object files from
different compiler versions and all that is required for correctness is that
the runtime libraries must continue to provide all previous versions.

What you're doing here doesn't allow the format of index {1,2,3} to *ever*
change.  You've fixed it forever, barring abandoning those indices and always
using index 0.

I know that the ARM EH format made exactly the same mistake, but let's see if
we can find some way of not replicating it, eh?


> @@ -206,4 +206,14 @@ _Unwind_SetIP (struct _Unwind_Context *context, _U
>    return __libunwind_Unwind_SetIP (context, val);
>  }
>  symver (_Unwind_SetIP, GCC_3.0);
> +
> +extern unsigned char __libunwind_Unwind_GetEhEncoding
> +  (struct _Unwind_Context *);
> +
> +unsigned char
> +_Unwind_GetEhEncoding (struct _Unwind_Context *context)
> +{
> +  return __libunwind_Unwind_GetEhEncoding (context, val);
> +}
> +symver (_Unwind_GetEhEncoding, GCC_3.0);
>  #endif

There is no such __libunwind symbol, is there?

> +mcompact-eh
> +Target Var(TARGET_COMPACT_EH) Init(1)
> +Use compact exception unwinding tables.

This ought to be automatically disabled with -fasynchronous-unwind-tables.

>  	  /* "Save" $sp in itself so we don't use the fake CFA.
>  	     This is: DW_CFA_val_expression r29, { DW_OP_reg29 }.  */
> -	  fprintf (asm_out_file, "\t.cfi_escape 0x16,29,1,0x6d\n");
> +	  if (dwarf2out_do_frame ()
> +	      && (flag_unwind_tables || flag_exceptions))
> +	    /* "Save" $sp in itself so we don't use the fake CFA.
> +	       This is: DW_CFA_val_expression r29, { DW_OP_reg29 }.  */
> +	    fprintf (asm_out_file, "\t.cfi_fde_data 0x5b\n");
> +	  else
> +	    fprintf (asm_out_file, "\t.cfi_escape 0x16,29,1,0x6d\n");

The commentary appears to be messed up?

> +  /* Place eh-related data in sdata so that we can use a gprel32 reloc
> +     to access it.  */

I thought sdata was reserved for 16-bit offsets.  Have things changed over the
years for the mips target?


r~

^ permalink raw reply	[flat|nested] 14+ messages in thread

* RE: [RFA] Compact EH Patch
  2015-09-18 19:34 ` Richard Henderson
@ 2015-10-05 23:14   ` Moore, Catherine
  2015-10-06 16:12     ` Richard Henderson
  2015-11-25 17:13   ` Moore, Catherine
  2015-12-13 22:12   ` Moore, Catherine
  2 siblings, 1 reply; 14+ messages in thread
From: Moore, Catherine @ 2015-10-05 23:14 UTC (permalink / raw)
  To: Richard Henderson, gcc-patches; +Cc: jason, Matthew Fortune


> -----Original Message-----
> From: Richard Henderson [mailto:rth@redhat.com]
> Subject: Re: [RFA] Compact EH Patch
> 

Richard, Thanks for the patch review.  
Matthew, Would you please take a look at the MIPS-specific pieces before I resubmit the patch?
There will be a small change to the data encoding for the MIPS-Linux toolchain to fix a problem that we found in the mabi=64 libraries.
 A follow-on patch to binutils will also be required.  I'll do an update to this patch, soon, including any changes for the MIPS backend.

Please see the embedded comments and a couple of questions.

Thanks,
Catherine

> > Index: libgcc/libgcc-std.ver.in
> >
> ==========================================================
> =========
> > --- libgcc/libgcc-std.ver.in	(revision 226409)
> > +++ libgcc/libgcc-std.ver.in	(working copy)
> > @@ -1918,6 +1918,7 @@ GCC_4.6.0 {
> >    __morestack_current_segment
> >    __morestack_initial_sp
> >    __splitstack_find
> > +  _Unwind_GetEhEncoding
> >  }
> >
> >  %inherit GCC_4.7.0 GCC_4.6.0
> > @@ -1938,3 +1939,8 @@ GCC_4.7.0 {
> >  %inherit GCC_4.8.0 GCC_4.7.0
> >  GCC_4.8.0 {
> >  }
> > +
> > +%inherit GCC_4.8.0 GCC_4.7.0
> > +GCC_4.8.0 {
> > +  __register_frame_info_header_bases
> > +}
> 
> You can't push new symbols into old versions.  These have to go into the
> version for the current gcc.

Yep, okay.  I'll fix these in the next version.
> 
> Likewise.
> 
> > +  if (data.type != CET_not_found)
> > +    return data.type;
> > +
> > +  return CET_not_found;
> 
> Return data.type unconditionally.

OK.
> 
> > +++ libgcc/unwind-pe.h	(working copy)
> > @@ -44,6 +44,7 @@
> >  #define DW_EH_PE_udata2         0x02
> >  #define DW_EH_PE_udata4         0x03
> >  #define DW_EH_PE_udata8         0x04
> > +#define DW_EH_PE_udata1         0x05
> >  #define DW_EH_PE_sleb128        0x09
> >  #define DW_EH_PE_sdata2         0x0A
> >  #define DW_EH_PE_sdata4         0x0B
> 
> If we're going to add udata1, we might as well add sdata1 for consistency.

OK.
> 
> > @@ -184,6 +187,7 @@ read_encoded_value_with_base (unsigned char
> encodi
> >    union unaligned
> >      {
> >        void *ptr;
> > +      unsigned u1 __attribute__ ((mode (QI)));
> >        unsigned u2 __attribute__ ((mode (HI)));
> >        unsigned u4 __attribute__ ((mode (SI)));
> >        unsigned u8 __attribute__ ((mode (DI)));
> 
> This is silly.  Access to a single byte never requires alignment help from the
> compiler...

OK.
> 
> > +	case DW_EH_PE_udata1:
> > +	  result = u->u1;
> > +	  p += 1;
> > +	  break;
> 
>     result = *p;
> 
> > +  read_encoded_value_with_base (DW_EH_PE_absptr |
> DW_EH_PE_udata4, 0,
> > +				hdr + 4, &nrec);
> 
> Err... that encoding type makes no sense: absptr is "pointer size".  Combining
> that with an explicit size, like udata4, means that udata4 wins.  So this is the
> same as simply writing DW_EH_PE_udata4.
> 
> At which point you're loading a 4 byte unsigned quantity from an aligned
> address.  You might as well use *(uint32_t *)(hdr + 4).
> 

Ok, here as well.

> > +__register_frame_info_header_bases (const void *begin, struct object
> *ob,
> > +				    void *tbase, void *dbase)
> > +{
> > +  /* Only register compact EH frame headers.  */
> > +  if (begin == NULL || *(const char *) begin != 2)
> > +    return;
> 
> Check for no entries before registering?

That's reasonable.

> 
> > +extern char __GNU_EH_FRAME_HDR[] TARGET_ATTRIBUTE_WEAK;
> 
> Don't you have some guaranteed alignment for this table?
> That perhaps ought to be seen in either the type or an attribute.
> 

OK.
> +      if (op < 0x40)
> +      else if (op < 0x48)
> +      else if (op < 0x50)
> +      else if (op < 0x58)
> +      else if (op == 0x58)
> +      else if (op == 0x59)
> +      else if (op == 0x5a)
> +      else if (op == 0x5b)
> +      else if (op == 0x5c)
> +      else if (op == 0x5d)
> +      else if (op == 0x5e)
> +      else if (op == 0x5f)
> +      else if (op >= 0x60 && op < 0x6c)
> 
> Better as a switch statement surely, using the gcc case x ... y: extension.
OK.
> 
> > +static _Unwind_Reason_Code
> > +uw_frame_state_compact (struct _Unwind_Context *context,
> > +			_Unwind_FrameState *fs,
> > +			enum compact_entry_type entry_type,
> > +		       	struct compact_eh_bases *bases) {
> > +  const unsigned char *p;
> > +  unsigned int pr_index;
> > +  _Unwind_Ptr personality;
> > +  unsigned char buf[4];
> > +  _Unwind_Reason_Code rc;
> > +
> > +  p = bases->entry;
> > +  pr_index = *(p++);
> > +  switch (pr_index) {
> > +  case 0:
> > +      p = read_encoded_value (context, bases->eh_encoding, p,
> &personality);
> > +      fs->personality = (_Unwind_Personality_Fn) personality;
> > +      break;
> > +  case 1:
> > +      fs->personality = __gnu_compact_pr1;
> > +      break;
> > +  case 2:
> > +      fs->personality = __gnu_compact_pr2;
> > +      break;
> > +  default:
> > +      fs->personality = NULL;
> > +  }
> 
> This is the aspect of this spec about which I am least keen.  The existing
> method whereby the personality function is explicit in each object file means
> that we've got automatic version control on data that is private to the object
> file.
> 
> That is, if we should ever change the format of the LSDA -- as in fact you are
> doing here -- then all we need do is change __gcc_personality_v0 to
> __gcc_personality_v1, and all is well.  One can mix and match object files
> from different compiler versions and all that is required for correctness is
> that the runtime libraries must continue to provide all previous versions.
> 
> What you're doing here doesn't allow the format of index {1,2,3} to *ever*
> change.  You've fixed it forever, barring abandoning those indices and always
> using index 0.
> 
> I know that the ARM EH format made exactly the same mistake, but let's see
> if we can find some way of not replicating it, eh?

I'm not 100% sure what you are suggesting here.  Would you like to see __gnu_compact_pr2_v0,  etc?  
Are you also saying that you want to see a undefined reference to the personality routine in the object file?

> 
> 
> > @@ -206,4 +206,14 @@ _Unwind_SetIP (struct _Unwind_Context
> *context, _U
> >    return __libunwind_Unwind_SetIP (context, val);  }  symver
> > (_Unwind_SetIP, GCC_3.0);
> > +
> > +extern unsigned char __libunwind_Unwind_GetEhEncoding
> > +  (struct _Unwind_Context *);
> > +
> > +unsigned char
> > +_Unwind_GetEhEncoding (struct _Unwind_Context *context) {
> > +  return __libunwind_Unwind_GetEhEncoding (context, val); } symver
> > +(_Unwind_GetEhEncoding, GCC_3.0);
> >  #endif
> 
> There is no such __libunwind symbol, is there?

No there isn't.    I'll remove this.
> 
> > +mcompact-eh
> > +Target Var(TARGET_COMPACT_EH) Init(1) Use compact exception
> unwinding
> > +tables.
> 
> This ought to be automatically disabled with -fasynchronous-unwind-tables.

No problem, I can do this.

> 
> >  	  /* "Save" $sp in itself so we don't use the fake CFA.
> >  	     This is: DW_CFA_val_expression r29, { DW_OP_reg29 }.  */
> > -	  fprintf (asm_out_file, "\t.cfi_escape 0x16,29,1,0x6d\n");
> > +	  if (dwarf2out_do_frame ()
> > +	      && (flag_unwind_tables || flag_exceptions))
> > +	    /* "Save" $sp in itself so we don't use the fake CFA.
> > +	       This is: DW_CFA_val_expression r29, { DW_OP_reg29 }.  */
> > +	    fprintf (asm_out_file, "\t.cfi_fde_data 0x5b\n");
> > +	  else
> > +	    fprintf (asm_out_file, "\t.cfi_escape 0x16,29,1,0x6d\n");
> 
> The commentary appears to be messed up?
> 
Yes, it is.


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFA] Compact EH Patch
  2015-10-05 23:14   ` Moore, Catherine
@ 2015-10-06 16:12     ` Richard Henderson
  0 siblings, 0 replies; 14+ messages in thread
From: Richard Henderson @ 2015-10-06 16:12 UTC (permalink / raw)
  To: Moore, Catherine, gcc-patches; +Cc: jason, Matthew Fortune

On 10/06/2015 10:14 AM, Moore, Catherine wrote:
>>> +  switch (pr_index) {
>>> +  case 0:
>>> +      p = read_encoded_value (context, bases->eh_encoding, p,
>> &personality);
>>> +      fs->personality = (_Unwind_Personality_Fn) personality;
>>> +      break;
>>> +  case 1:
>>> +      fs->personality = __gnu_compact_pr1;
>>> +      break;
>>> +  case 2:
>>> +      fs->personality = __gnu_compact_pr2;
>>> +      break;
>>> +  default:
>>> +      fs->personality = NULL;
>>> +  }
>>
>> This is the aspect of this spec about which I am least keen.  The existing
>> method whereby the personality function is explicit in each object file means
>> that we've got automatic version control on data that is private to the object
>> file.
>>
>> That is, if we should ever change the format of the LSDA -- as in fact you are
>> doing here -- then all we need do is change __gcc_personality_v0 to
>> __gcc_personality_v1, and all is well.  One can mix and match object files
>> from different compiler versions and all that is required for correctness is
>> that the runtime libraries must continue to provide all previous versions.
>>
>> What you're doing here doesn't allow the format of index {1,2,3} to *ever*
>> change.  You've fixed it forever, barring abandoning those indices and always
>> using index 0.
>>
>> I know that the ARM EH format made exactly the same mistake, but let's see
>> if we can find some way of not replicating it, eh?
>
> I'm not 100% sure what you are suggesting here.  Would you like to see __gnu_compact_pr2_v0,  etc?
> Are you also saying that you want to see a undefined reference to the personality routine in the object file?

I'm commenting on what I see as a defect in the spec, but not making a specific 
recommendation.

Using __gnu_compact_pr2_v0 etc doesn't help, because you're still using just 2 
bits to reference it.  The only real solution is a symbolic reference from the 
object file, which you get from pr_index == 0.


r~

^ permalink raw reply	[flat|nested] 14+ messages in thread

* RE: [RFA] Compact EH  Patch
  2015-07-30 21:07 [RFA] Compact EH Patch Moore, Catherine
                   ` (2 preceding siblings ...)
  2015-09-18 19:34 ` Richard Henderson
@ 2015-10-28 16:44 ` Matthew Fortune
  3 siblings, 0 replies; 14+ messages in thread
From: Matthew Fortune @ 2015-10-28 16:44 UTC (permalink / raw)
  To: Moore, Catherine, gcc-patches; +Cc: jason

> This patch implements a more compact format for exception handling data.  Although I don't
> have recent numbers for the amount of compression achieved, an earlier measurement showed
> a 30% reduction in the size of EH data for libstdc++.
> 
> A design document detailing the new format is available
> (https://github.com/MentorEmbedded/cxx-abi/blob/master/MIPSCompactEH.pdf).
> 
> This implementation enables the new format for MIPS targets only, but the generic pieces
> to enable the new format for other architectures is in place.
> I couldn't really think of a good way to split up the patch to make the review easier, but
> I am open to suggestions.
> I've successfully bootstrapped with the patch and completed testing across many of the
> MIPS targets and multilibs.
> I also ran a test series for the x86 without regressions.
> Thanks,
> Catherine

Hi Catherine,

I'd be lying if I said I understand all of this but I follow the bulk of what is needed
from the MIPS side of things. This looks fine to me. There is some code which may not
be MIPS specific in here, a request to change the magic constants to macros and comments
in the key function that actually generates compact EH entries (mips_cfi_endproc).

As this seems quite well separated from non-compact-eh cases then I am more than happy
to deal with any issues or refinement incrementally. I'd like to get such a big patch
committed sooner rather than later.

Comments inline.

Thanks,
Matthew

>Index: libgcc/config/mips/mips-unwind.h
>===================================================================
>--- libgcc/config/mips/mips-unwind.h	(revision 0)
>+++ libgcc/config/mips/mips-unwind.h	(revision 0)
>@@ -0,0 +1,182 @@
>+/* Compact EH unwinding support for MIPS.
>+   Copyright (C) 2012-2015 Free Software Foundation, Inc.
>+
>+This file is part of GCC.
>+
>+GCC is free software; you can redistribute it and/or modify
>+it under the terms of the GNU General Public License as published by
>+the Free Software Foundation; either version 3, or (at your option)
>+any later version.
>+
>+GCC is distributed in the hope that it will be useful,
>+but WITHOUT ANY WARRANTY; without even the implied warranty of
>+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+GNU General Public License for more details.
>+
>+Under Section 7 of GPL version 3, you are granted additional
>+permissions described in the GCC Runtime Library Exception, version
>+3.1, as published by the Free Software Foundation.
>+
>+You should have received a copy of the GNU General Public License and
>+a copy of the GCC Runtime Library Exception along with this program;
>+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>+<http://www.gnu.org/licenses/>.  */
>+
>+#ifdef MD_HAVE_COMPACT_EH
>+
>+#define DWARF_SP_REGNO 29
>+
>+#if _MIPS_SIM == _ABIO32
>+#define MIPS_EH_STACK_ALIGN 8
>+#else
>+#define MIPS_EH_STACK_ALIGN 16
>+#endif
>+
>+#define VRF_0 32
>+

The following two functions are missing a brief comment

>+static int
>+record_push (_Unwind_FrameState *fs, int reg, int offset)
>+{
>+  int idx = DWARF_REG_TO_UNWIND_COLUMN (reg);
>+
>+  offset -= dwarf_reg_size_table[idx];
>+  fs->regs.reg[idx].how = REG_SAVED_OFFSET;
>+  fs->regs.reg[idx].loc.offset = offset;
>+  return offset;
>+}
>+
>+static void
>+record_cfa_adjustment (_Unwind_FrameState *fs, _uleb128_t val)
>+{
>+  int i;
>+  fs->regs.cfa_offset += val;
>+  /* In case we see an adjustment after pushes, it means that
>+     the registers aren't saved at the top of the frame, probably
>+     due to a varargs save area.  Adjust the recorded offsets.  */
>+  for (i = 0; i < DWARF_FRAME_REGISTERS + 1; i++)
>+    if (fs->regs.reg[i].how == REG_SAVED_OFFSET)
>+      fs->regs.reg[i].loc.offset -= val;
>+}
>+
>+/* Process the frame unwinding opcodes.  */
>+
>+static _Unwind_Reason_Code
>+md_unwind_compact (struct _Unwind_Context *context ATTRIBUTE_UNUSED,
>+		   _Unwind_FrameState *fs, const unsigned char **pp)
>+{
>+  unsigned char op;
>+  _uleb128_t val;
>+  int push_offset;
>+  int i;
>+  int n;
>+  const unsigned char *p = *pp;
>+
>+  push_offset = 0;
>+  fs->regs.cfa_how = CFA_REG_OFFSET;
>+  fs->regs.cfa_reg = STACK_POINTER_REGNUM;
>+  fs->regs.cfa_offset = 0;
>+  fs->retaddr_column = 31;
>+
>+  while (1)
>+    {
>+      op = *(p++);
>+
>+      if (op < 0x40)

Can we get these magic constants converted to some macros? I think there are sensible
names they could be given.

>+	{
>+	  /* Increment stack pointer.  */
>+	  record_cfa_adjustment (fs, (op + 1) * MIPS_EH_STACK_ALIGN);
>+	}
>+      else if (op < 0x48)
>+	{
>+	  /* Push VR[16] to VR[16+x] and VR[31] */
>+	  push_offset = record_push (fs, 31, push_offset);
>+	  for (i = op & 7; i >= 0; i--)
>+	    push_offset = record_push (fs, 16 + i, push_offset);
>+	}
>+      else if (op < 0x50)
>+	{
>+	  /* Push VR[16] to VR[16+x], VR[30] and VR[31] */
>+	  push_offset = record_push (fs, 31, push_offset);
>+	  push_offset = record_push (fs, 30, push_offset);
>+	  for (i = op & 7; i >= 0; i--)
>+	    push_offset = record_push (fs, 16 + i, push_offset);
>+	}
>+      else if (op < 0x58)
>+	{
>+	  /* Restore stack ponter from frame pointer */
>+	  fs->regs.cfa_reg = (op & 7) + 16;
>+	  fs->regs.cfa_offset = 0;
>+	}
>+      else if (op == 0x58)
>+       	{

indentation

>+	  /* Large SP increment.  */
>+	  p = read_uleb128 (p, &val);
>+	  record_cfa_adjustment (fs, (val + 129) * MIPS_EH_STACK_ALIGN);
>+	}
>+      else if (op == 0x59)
>+	{
>+	  /* Push VR[x] to VR[x+y] */
>+	  op = *(p++);
>+	  n = op >> 3;
>+	  for (i = op & 7; i >= 0; i--)
>+	    push_offset = record_push (fs, n + i, push_offset);
>+	}
>+      else if (op == 0x5a)
>+	{
>+	  /* Push VRF[x] to VRF[x+y] */
>+	  op = *(p++);
>+	  n = (op >> 3) + VRF_0;
>+	  for (i = op & 7; i >= 0; i--)
>+	    push_offset = record_push (fs, n + i, push_offset);
>+	}
>+      else if (op == 0x5b)
>+	{
>+	  /* Restore the CFA to stack pointer.  */
>+	  fs->regs.cfa_reg = STACK_POINTER_REGNUM;
>+	  fs->regs.cfa_offset = 0;
>+	}
>+

spurious newline.

>+      else if (op == 0x5c)
>+	{
>+	  /* Finish.  */
>+	  *pp = p;
>+	  return _URC_NO_REASON;
>+	}
>+      else if (op == 0x5d)
>+	{
>+	  /* No unwind.  */
>+	  return _URC_END_OF_STACK;
>+	}
>+      else if (op == 0x5e)
>+	{
>+	  /* Restore SP from VR[30] */
>+	  fs->regs.cfa_reg = 30;
>+	  fs->regs.cfa_offset = 0;
>+	}
>+      else if (op == 0x5f)
>+	{
>+	  /* NOP */
>+	}
>+      else if (op >= 0x60 && op < 0x6c)
>+	{
>+	  /* Push VRF[20] to VRF[20 + x] */
>+	  for (i = op & 0xf; i >= 0; i--)
>+	    push_offset = record_push (fs, VRF_0 + 20 + i, push_offset);
>+	}
>+      else if (op >= 0x6c && op < 0x70)
>+	{
>+	  /* MIPS16 push VR[16], VR[17], VR[18+x]-VR[23], VR[31] */
>+	  push_offset = record_push (fs, 31, push_offset);
>+	  for (i = 23; i >= 18 + (op & 3); i--)
>+	    push_offset = record_push (fs, i, push_offset);
>+	  push_offset = record_push (fs, 17, push_offset);
>+	  push_offset = record_push (fs, 16, push_offset);
>+	}
>+      else
>+	{
>+	  return _URC_FATAL_PHASE1_ERROR;
>+	}

No braces required above.

>+    }
>+}
>+
>+#endif /* MD_HAVE_COMPACT_EH */
>Index: libgcc/config/mips/mips16.S
>===================================================================
>--- libgcc/config/mips/mips16.S	(revision 226409)
>+++ libgcc/config/mips/mips16.S	(working copy)
>@@ -624,6 +624,18 @@ CALL_STUB_NO_RET (__mips16_call_stub_10, 10)
>    being called is 16 bits, in which case the copy is unnecessary;
>    however, it's faster to always do the copy.  */
> 
>+#ifdef __GNU_COMPACT_EH__
>+#define CALL_STUB_RET(NAME, CODE, MODE)	\
>+STARTFN (NAME);				\
>+	move    $18,$31;		\
>+	STUB_ARGS_##CODE;		\
>+	.set    noreorder;		\
>+	jalr    $2;			\
>+	move    $25,$2;			\
>+	.set    reorder;		\
>+	MOVE_##MODE##_RET (f, $18);	\
>+	ENDFN (NAME)
>+#else

Does this mean you can't unwind through a MIPS16 stub with compact EH?

>Index: gcc/doc/invoke.texi
>===================================================================
>--- gcc/doc/invoke.texi	(revision 226409)
>+++ gcc/doc/invoke.texi	(working copy)
>@@ -814,7 +814,7 @@ Objective-C and Objective-C++ Dialects}.
> -mbranch-cost=@var{num}  -mbranch-likely  -mno-branch-likely @gol
> -mfp-exceptions -mno-fp-exceptions @gol
> -mvr4130-align -mno-vr4130-align -msynci -mno-synci @gol
>--mrelax-pic-calls -mno-relax-pic-calls -mmcount-ra-address}
>+-mrelax-pic-calls -mno-relax-pic-calls -mmcount-ra-address -mno-compact-eh}

Normally both the positive and negative options are quoted ih here.

>Index: gcc/config/mips/mips.opt
>===================================================================
>--- gcc/config/mips/mips.opt	(revision 226409)
>+++ gcc/config/mips/mips.opt	(working copy)
>@@ -99,6 +99,10 @@ Enum(mips_code_readable_setting) String(pcrel) Val
> EnumValue
> Enum(mips_code_readable_setting) String(no) Value(CODE_READABLE_NO)
> 
>+mcompact-eh
>+Target Var(TARGET_COMPACT_EH) Init(1)
>+Use compact exception unwinding tables.
>+

Should this be a MIPS specific option? Given there is also control for each
backend to define MD_HAVE_COMPACT_EH then I'd have thought this would be a
-f option.

>Index: gcc/config/mips/mips.c
>===================================================================
>--- gcc/config/mips/mips.c	(revision 226409)
>+++ gcc/config/mips/mips.c	(working copy)
>@@ -77,6 +77,7 @@ along with GCC; see the file COPYING3.  If not see
> #include "cgraph.h"
> #include "builtins.h"
> #include "rtl-iter.h"
>+#include "except.h"
> 
> /* This file should be included last.  */
> #include "target-def.h"
>@@ -693,6 +694,8 @@ static mips_one_only_stub *mips16_rdhwr_stub;
> static mips_one_only_stub *mips16_get_fcsr_stub;
> static mips_one_only_stub *mips16_set_fcsr_stub;
> 
>+static bool done_cfi_sections;
>+
> /* Index R is the smallest register class that contains register R.  */
> const enum reg_class mips_regno_to_class[FIRST_PSEUDO_REGISTER] = {
>   LEA_REGS,        LEA_REGS,        M16_STORE_REGS,  V1_REG,
>@@ -6669,6 +6672,40 @@ mips_start_unique_function (const char *name)
>   putc ('\n', asm_out_file);
> }
> 
>+/* The LTO frontend only enables exceptions when it sees a function that
>+   uses it.  This changes the return value of dwarf2out_do_frame, so we
>+   have to check before every function.  */
>+
>+void
>+mips_fixup_cfi_sections (void)
>+{
>+#ifdef MD_HAVE_COMPACT_EH
>+  if (done_cfi_sections)
>+    return;
>+
>+  if (!TARGET_COMPACT_EH)
>+    return;
>+
>+  /* Output a .cfi_sections directive.  */
>+  if (dwarf2out_do_frame ())
>+    {
>+      if (flag_unwind_tables || flag_exceptions)
>+	{
>+	  if (write_symbols == DWARF2_DEBUG
>+	      || write_symbols == VMS_AND_DWARF2_DEBUG)
>+	    fprintf (asm_out_file,
>+		     "\t.cfi_sections .debug_frame, .eh_frame_entry\n");
>+	  else
>+	    fprintf (asm_out_file, "\t.cfi_sections .eh_frame_entry\n");
>+	}
>+      else
>+	fprintf (asm_out_file, "\t.cfi_sections .debug_frame\n");
>+      done_cfi_sections = true;
>+    }
>+#endif
>+}
>+
>+

Double newline.  This code does not seem MIPS specific and is likely to be
duplicated for any other target with compact-eh.  I'm not going to insist on
moving it out of here but I hope that when another arch uses compact-eh then
this code will be moved.

> /* Start a definition of function NAME.  MIPS16_P indicates whether the
>    function contains MIPS16 code.  */
> 
>@@ -7252,7 +7289,13 @@ mips16_build_call_stub (rtx retval, rtx *fn_ptr, r
> 
> 	  /* "Save" $sp in itself so we don't use the fake CFA.
> 	     This is: DW_CFA_val_expression r29, { DW_OP_reg29 }.  */

This comment looks like it ends up duplicated below.

>-	  fprintf (asm_out_file, "\t.cfi_escape 0x16,29,1,0x6d\n");
>+	  if (dwarf2out_do_frame ()
>+	      && (flag_unwind_tables || flag_exceptions))
>+	    /* "Save" $sp in itself so we don't use the fake CFA.
>+	       This is: DW_CFA_val_expression r29, { DW_OP_reg29 }.  */
>+	    fprintf (asm_out_file, "\t.cfi_fde_data 0x5b\n");
>+	  else
>+	    fprintf (asm_out_file, "\t.cfi_escape 0x16,29,1,0x6d\n");
> 
> 	  /* Save the return address in $18.  The stub's caller knows
> 	     that $18 might be clobbered, even though $18 is usually
>@@ -8850,6 +8893,19 @@ mips_function_rodata_section (tree decl)
>   return data_section;
> }
> 
>+/* Implement TARGET_ASM_INIT_SECTIONS.  */
>+
>+static void
>+mips_asm_init_sections (void)
>+{
>+  if (TARGET_COMPACT_EH)
>+    {
>+      /* Let the assembler decide where to put the LSDA.  */
>+      exception_section = get_unnamed_section (0, output_section_asm_op,
>+					       "\t.cfi_inline_lsda 2");
>+    }
>+}

I'm not sure this is really MIPS specific either. Same comment as before.

>@@ -12175,6 +12240,175 @@ mips_expand_epilogue (bool sibcall_p)
>       emit_insn_before (gen_mips_ehb (), insn);
>     }
> }
>+
>+/* Add an opcode to *FDE to describe the necessary pushes of r30/r31, if
>+   any.  */
>+static void
>+add_fp_ra_push (vec<uchar, va_gc> **fde)
>+{
>+  bool fp_needed = BITSET_P (cfun->machine->frame.mask, 30);
>+  if (!BITSET_P (cfun->machine->frame.mask, RETURN_ADDR_REGNUM)
>+      && !fp_needed)
>+    return;
>+
>+  vec_safe_push (*fde, (uchar) 0x59);

Magic number again here that could have a meaningful name.

>+  if (fp_needed)
>+    vec_safe_push (*fde, (uchar) ((30 << 3) | 1));
>+  else
>+    vec_safe_push (*fde, uchar (31 << 3));

Does this compile? Shouldn't it be (uchar)?

>+}
>+
>+/* Add an opcode to *FDE to describe an adjustment of the CFA register by
>+   AMOUNT, given an assumed alignment ALIGN.  */
>+static void
>+mips_fdedata_cfaadjust (vec <uchar, va_gc> **fde,
>+			HOST_WIDE_INT amount, int align)
>+{
>+  if (amount == 0)
>+    return;
>+
>+  gcc_assert (amount % align == 0);
>+  if (amount >= align * 129)
>+    {
>+      vec_safe_push (*fde, (uchar) 0x58);

Magic constants.

>+      push_uleb128 (fde, amount / align - 129);
>+    }
>+  else if (amount >= align * 65)
>+    {
>+      vec_safe_push (*fde, (uchar) 0x3f);
>+      vec_safe_push (*fde, (uchar) (amount / align - 64));
>+    }
>+  else
>+    vec_safe_push (*fde, (uchar) (amount / align - 1));
>+}
>+
>+/* Implements TARGET_ASM_OUTPUT_CFI_ENDPROC.  Emit the .cfi_endproc
>+   directive, and when emitting unwind information, also emit the
>+   .cfi_fde_data directive.  */
>+static void
>+mips_cfi_endproc (void)
>+{
>+  int align = TARGET_NEWABI ? 16 : 8;

Is this stack alignment? If so (or anything else that relates to ABI) then
there should be a macro you can reuse for this instead of inferring it from
TARGET_NEWABI.

There should be a check somewhere to inhibit the use of compact-eh with
anything other than o32/n32/n64 I think as there are many assumptions about
the ABI being one of these.

This function could do with a lot more comments to explain the process like
a step by step description of what is handled at each point.

>+  vec<uchar, va_gc> *fde = NULL;
>+  int i, len, n;
>+  HOST_WIDE_INT total, push_base;
>+  bool any_saved = false;
>+
>+  if (!TARGET_COMPACT_EH
>+      || (!flag_unwind_tables && !flag_exceptions)
>+      || flag_asynchronous_unwind_tables)
>+    {
>+      fprintf (asm_out_file, ".cfi_endproc\n");
>+      return;
>+    }
>+
>+  fprintf (asm_out_file, "\t.cfi_fde_data ");
>+
>+  total = cfun->machine->frame.total_size;
>+  if (cfun->machine->frame.num_fp > 0)
>+    push_base = cfun->machine->frame.fp_sp_offset + UNITS_PER_HWFPVALUE;
>+  else if (cfun->machine->frame.num_gp > 0)
>+    push_base = cfun->machine->frame.gp_sp_offset + UNITS_PER_WORD;
>+  else
>+    push_base = total;
>+
>+  if (frame_pointer_needed)
>+    {
>+      if (HARD_FRAME_POINTER_REGNUM == 30)
>+	vec_safe_push (fde, (uchar) 0x5E);
>+      else
>+	{
>+	  int t = HARD_FRAME_POINTER_REGNUM - 16;
>+	  gcc_assert (t < 8);
>+	  vec_safe_push (fde, (uchar) (0x50 + t));
>+	}
>+      total -= cfun->machine->frame.hard_frame_pointer_offset;
>+      push_base -= cfun->machine->frame.hard_frame_pointer_offset;
>+    }
>+
>+  if (HARD_FRAME_POINTER_REGNUM == 30 && frame_pointer_needed)
>+    gcc_assert (BITSET_P (cfun->machine->frame.mask, 30));
>+
>+  mips_fdedata_cfaadjust (&fde, push_base, align);
>+  total -= push_base;
>+
>+  n = 0;
>+  for (i = 31; i > 19; i--)
>+    {
>+      bool isset = BITSET_P (cfun->machine->frame.fmask, i);
>+      if (isset)
>+	n++;
>+      if ((i == 20 || !isset) && n > 0)
>+	{
>+	  int first = i + (isset ? 0 : 1);
>+	  if (first == 20)
>+	    {
>+	      if (n > 8)
>+		vec_safe_push (fde, (uchar) (0x68 + n - 9));
>+	      else
>+		vec_safe_push (fde, (uchar) (0x60 + n - 1));
>+	    }
>+	  else
>+	    {
>+	      if (n > 8)
>+		{
>+		  vec_safe_push (fde, (uchar) 0x5a);
>+		  vec_safe_push (fde, (uchar) (((first + 8) << 3) | (n - 9)));
>+		  n = 8;
>+		}
>+	      vec_safe_push (fde, (uchar) 0x5a);
>+	      vec_safe_push (fde, (uchar) ((first << 3) | (n - 1)));
>+	    }
>+	  n = 0;
>+	}
>+    }
>+  gcc_assert (n == 0);
>+
>+  for (i = 23; i > 0; i--)
>+    {
>+      bool isset = BITSET_P (cfun->machine->frame.mask, i);
>+      if (isset)
>+	n++;
>+      if ((i == 16 || i == 1 || !isset) && n > 0)
>+	{
>+	  int first = i + (isset ? 0 : 1);
>+	  if (first == 16 && !any_saved
>+	      && BITSET_P (cfun->machine->frame.mask, RETURN_ADDR_REGNUM))
>+	    {
>+	      if (BITSET_P (cfun->machine->frame.mask, 30))
>+		vec_safe_push (fde, (uchar) (0x48 + n - 1));
>+	      else
>+		vec_safe_push (fde, (uchar) (0x40 + n - 1));
>+	    }
>+	  else
>+	    {
>+	      if (!any_saved)
>+		add_fp_ra_push (&fde);
>+	      vec_safe_push (fde, (uchar) 0x59);
>+	      vec_safe_push (fde, (uchar) ((first << 3) | (n - 1)));
>+	    }
>+	  any_saved = true;
>+	  n = 0;
>+	}
>+    }
>+  gcc_assert (n == 0);
>+
>+  if (!any_saved)
>+    add_fp_ra_push (&fde);
>+
>+  /* Skip varargs save area and suchlike.  */
>+  mips_fdedata_cfaadjust (&fde, total, align);
>+
>+  /* The assembler appends a "Finish" opcode for out-of-line entries;
>+     the unwind code (uw_frame_state_compact) for inline entries.  */
>+
>+  len = vec_safe_length (fde);
>+  for (i = 0; i < len; i++)
>+    fprintf (asm_out_file, "0x%x%s", (*fde)[i],
>+	     i + 1 == len ? "" : ",");
>+  fputc ('\n', asm_out_file);
>+  fprintf (asm_out_file, "\t.cfi_endproc\n");
>+}
> 
> /* Return nonzero if this function is known to have a null epilogue.
>    This allows the optimizer to omit jumps to jumps if no stack

^ permalink raw reply	[flat|nested] 14+ messages in thread

* RE: [RFA] Compact EH Patch
  2015-09-18 19:34 ` Richard Henderson
  2015-10-05 23:14   ` Moore, Catherine
@ 2015-11-25 17:13   ` Moore, Catherine
  2015-12-01 21:32     ` Moore, Catherine
  2015-12-01 21:33     ` Jason Merrill
  2015-12-13 22:12   ` Moore, Catherine
  2 siblings, 2 replies; 14+ messages in thread
From: Moore, Catherine @ 2015-11-25 17:13 UTC (permalink / raw)
  To: Richard Henderson, gcc-patches; +Cc: jason, Matthew Fortune



> -----Original Message-----
> From: Richard Henderson [mailto:rth@redhat.com]
> Sent: Friday, September 18, 2015 3:25 PM
> To: Moore, Catherine; gcc-patches@gcc.gnu.org
> Cc: jason@redhat.com; Matthew Fortune
> Subject: Re: [RFA] Compact EH Patch
> 
> > Index: libgcc/libgcc-std.ver.in
> >
> ==========================================================
> =========
> > --- libgcc/libgcc-std.ver.in	(revision 226409)
> > +++ libgcc/libgcc-std.ver.in	(working copy)
> > @@ -1918,6 +1918,7 @@ GCC_4.6.0 {
> >    __morestack_current_segment
> >    __morestack_initial_sp
> >    __splitstack_find
> > +  _Unwind_GetEhEncoding
> >  }
> >
> >  %inherit GCC_4.7.0 GCC_4.6.0
> > @@ -1938,3 +1939,8 @@ GCC_4.7.0 {
> >  %inherit GCC_4.8.0 GCC_4.7.0
> >  GCC_4.8.0 {
> >  }
> > +
> > +%inherit GCC_4.8.0 GCC_4.7.0
> > +GCC_4.8.0 {
> > +  __register_frame_info_header_bases
> > +}
> 
> You can't push new symbols into old versions.  These have to go into the
> version for the current gcc.
> 
> > Index: libstdc++-v3/config/abi/pre/gnu.ver
> >
> ==========================================================
> =========
> > --- libstdc++-v3/config/abi/pre/gnu.ver	(revision 226409)
> > +++ libstdc++-v3/config/abi/pre/gnu.ver	(working copy)
> > @@ -1909,6 +1909,7 @@ CXXABI_1.3 {
> >      __gxx_personality_v0;
> >      __gxx_personality_sj0;
> >      __gxx_personality_seh0;
> > +    __gnu_compact_pr2;
> >      __dynamic_cast;
> >
> >      # *_type_info classes, ctor and dtor
> > Index: libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
> >
> ==========================================================
> =========
> > --- libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
> 	(revision 226409)
> > +++ libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
> 	(working copy)
> > @@ -200,6 +200,7 @@ CXXABI_2.0 {
> >      __cxa_vec_new;
> >      __gxx_personality_v0;
> >      __gxx_personality_sj0;
> > +    __gnu_compact_pr2;
> >      __dynamic_cast;
> >
> >      # std::exception_ptr
> 
> Likewise.
> 
I'm getting ready to post the updates to this patch -- hopefully, I can still get it in GCC 6.0.
I'm not sure how to tell what the current CXXABI is for these two files.  Should it be CXXABI_2.0 for both of these?
Thanks,
Catherine

^ permalink raw reply	[flat|nested] 14+ messages in thread

* RE: [RFA] Compact EH Patch
  2015-11-25 17:13   ` Moore, Catherine
@ 2015-12-01 21:32     ` Moore, Catherine
  2015-12-01 21:33     ` Jason Merrill
  1 sibling, 0 replies; 14+ messages in thread
From: Moore, Catherine @ 2015-12-01 21:32 UTC (permalink / raw)
  To: Richard Henderson, gcc-patches; +Cc: jason, Matthew Fortune

Ping?

> -----Original Message-----
> From: gcc-patches-owner@gcc.gnu.org [mailto:gcc-patches-
> owner@gcc.gnu.org] On Behalf Of Moore, Catherine
> Sent: Wednesday, November 25, 2015 11:58 AM
> To: Richard Henderson; gcc-patches@gcc.gnu.org
> Cc: jason@redhat.com; Matthew Fortune
> Subject: RE: [RFA] Compact EH Patch
> 
> 
> 
> > -----Original Message-----
> > From: Richard Henderson [mailto:rth@redhat.com]
> > Sent: Friday, September 18, 2015 3:25 PM
> > To: Moore, Catherine; gcc-patches@gcc.gnu.org
> > Cc: jason@redhat.com; Matthew Fortune
> > Subject: Re: [RFA] Compact EH Patch
> >
> > > Index: libgcc/libgcc-std.ver.in
> > >
> >
> ==========================================================
> > =========
> > > --- libgcc/libgcc-std.ver.in	(revision 226409)
> > > +++ libgcc/libgcc-std.ver.in	(working copy)
> > > @@ -1918,6 +1918,7 @@ GCC_4.6.0 {
> > >    __morestack_current_segment
> > >    __morestack_initial_sp
> > >    __splitstack_find
> > > +  _Unwind_GetEhEncoding
> > >  }
> > >
> > >  %inherit GCC_4.7.0 GCC_4.6.0
> > > @@ -1938,3 +1939,8 @@ GCC_4.7.0 {
> > >  %inherit GCC_4.8.0 GCC_4.7.0
> > >  GCC_4.8.0 {
> > >  }
> > > +
> > > +%inherit GCC_4.8.0 GCC_4.7.0
> > > +GCC_4.8.0 {
> > > +  __register_frame_info_header_bases
> > > +}
> >
> > You can't push new symbols into old versions.  These have to go into
> > the version for the current gcc.
> >
> > > Index: libstdc++-v3/config/abi/pre/gnu.ver
> > >
> >
> ==========================================================
> > =========
> > > --- libstdc++-v3/config/abi/pre/gnu.ver	(revision 226409)
> > > +++ libstdc++-v3/config/abi/pre/gnu.ver	(working copy)
> > > @@ -1909,6 +1909,7 @@ CXXABI_1.3 {
> > >      __gxx_personality_v0;
> > >      __gxx_personality_sj0;
> > >      __gxx_personality_seh0;
> > > +    __gnu_compact_pr2;
> > >      __dynamic_cast;
> > >
> > >      # *_type_info classes, ctor and dtor
> > > Index: libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
> > >
> >
> ==========================================================
> > =========
> > > --- libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
> > 	(revision 226409)
> > > +++ libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
> > 	(working copy)
> > > @@ -200,6 +200,7 @@ CXXABI_2.0 {
> > >      __cxa_vec_new;
> > >      __gxx_personality_v0;
> > >      __gxx_personality_sj0;
> > > +    __gnu_compact_pr2;
> > >      __dynamic_cast;
> > >
> > >      # std::exception_ptr
> >
> > Likewise.
> >
> I'm getting ready to post the updates to this patch -- hopefully, I can still get it
> in GCC 6.0.
> I'm not sure how to tell what the current CXXABI is for these two files.
> Should it be CXXABI_2.0 for both of these?
> Thanks,
> Catherine

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFA] Compact EH Patch
  2015-11-25 17:13   ` Moore, Catherine
  2015-12-01 21:32     ` Moore, Catherine
@ 2015-12-01 21:33     ` Jason Merrill
  2015-12-02 13:39       ` Jonathan Wakely
  1 sibling, 1 reply; 14+ messages in thread
From: Jason Merrill @ 2015-12-01 21:33 UTC (permalink / raw)
  To: Moore, Catherine, Richard Henderson, gcc-patches
  Cc: Matthew Fortune, Jonathan Wakely

On 11/25/2015 11:58 AM, Moore, Catherine wrote:
>
>
>> -----Original Message-----
>> From: Richard Henderson [mailto:rth@redhat.com]
>> Sent: Friday, September 18, 2015 3:25 PM
>> To: Moore, Catherine; gcc-patches@gcc.gnu.org
>> Cc: jason@redhat.com; Matthew Fortune
>> Subject: Re: [RFA] Compact EH Patch
>>
>>> Index: libgcc/libgcc-std.ver.in
>>>
>> ==========================================================
>> =========
>>> --- libgcc/libgcc-std.ver.in	(revision 226409)
>>> +++ libgcc/libgcc-std.ver.in	(working copy)
>>> @@ -1918,6 +1918,7 @@ GCC_4.6.0 {
>>>     __morestack_current_segment
>>>     __morestack_initial_sp
>>>     __splitstack_find
>>> +  _Unwind_GetEhEncoding
>>>   }
>>>
>>>   %inherit GCC_4.7.0 GCC_4.6.0
>>> @@ -1938,3 +1939,8 @@ GCC_4.7.0 {
>>>   %inherit GCC_4.8.0 GCC_4.7.0
>>>   GCC_4.8.0 {
>>>   }
>>> +
>>> +%inherit GCC_4.8.0 GCC_4.7.0
>>> +GCC_4.8.0 {
>>> +  __register_frame_info_header_bases
>>> +}
>>
>> You can't push new symbols into old versions.  These have to go into the
>> version for the current gcc.
>>
>>> Index: libstdc++-v3/config/abi/pre/gnu.ver
>>>
>> ==========================================================
>> =========
>>> --- libstdc++-v3/config/abi/pre/gnu.ver	(revision 226409)
>>> +++ libstdc++-v3/config/abi/pre/gnu.ver	(working copy)
>>> @@ -1909,6 +1909,7 @@ CXXABI_1.3 {
>>>       __gxx_personality_v0;
>>>       __gxx_personality_sj0;
>>>       __gxx_personality_seh0;
>>> +    __gnu_compact_pr2;
>>>       __dynamic_cast;
>>>
>>>       # *_type_info classes, ctor and dtor
>>> Index: libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
>>>
>> ==========================================================
>> =========
>>> --- libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
>> 	(revision 226409)
>>> +++ libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
>> 	(working copy)
>>> @@ -200,6 +200,7 @@ CXXABI_2.0 {
>>>       __cxa_vec_new;
>>>       __gxx_personality_v0;
>>>       __gxx_personality_sj0;
>>> +    __gnu_compact_pr2;
>>>       __dynamic_cast;
>>>
>>>       # std::exception_ptr
>>
>> Likewise.
>>
> I'm getting ready to post the updates to this patch -- hopefully, I can still get it in GCC 6.0.
> I'm not sure how to tell what the current CXXABI is for these two files.  Should it be CXXABI_2.0 for both of these?

Jonathan, can you answer this question?

Jason

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFA] Compact EH Patch
  2015-12-01 21:33     ` Jason Merrill
@ 2015-12-02 13:39       ` Jonathan Wakely
  0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Wakely @ 2015-12-02 13:39 UTC (permalink / raw)
  To: Jason Merrill
  Cc: Moore, Catherine, Richard Henderson, gcc-patches, Matthew Fortune

On 01/12/15 16:33 -0500, Jason Merrill wrote:
>On 11/25/2015 11:58 AM, Moore, Catherine wrote:
>>
>>
>>>-----Original Message-----
>>>From: Richard Henderson [mailto:rth@redhat.com]
>>>Sent: Friday, September 18, 2015 3:25 PM
>>>To: Moore, Catherine; gcc-patches@gcc.gnu.org
>>>Cc: jason@redhat.com; Matthew Fortune
>>>Subject: Re: [RFA] Compact EH Patch
>>>
>>>>Index: libgcc/libgcc-std.ver.in
>>>>
>>>==========================================================
>>>=========
>>>>--- libgcc/libgcc-std.ver.in	(revision 226409)
>>>>+++ libgcc/libgcc-std.ver.in	(working copy)
>>>>@@ -1918,6 +1918,7 @@ GCC_4.6.0 {
>>>>    __morestack_current_segment
>>>>    __morestack_initial_sp
>>>>    __splitstack_find
>>>>+  _Unwind_GetEhEncoding
>>>>  }
>>>>
>>>>  %inherit GCC_4.7.0 GCC_4.6.0
>>>>@@ -1938,3 +1939,8 @@ GCC_4.7.0 {
>>>>  %inherit GCC_4.8.0 GCC_4.7.0
>>>>  GCC_4.8.0 {
>>>>  }
>>>>+
>>>>+%inherit GCC_4.8.0 GCC_4.7.0
>>>>+GCC_4.8.0 {
>>>>+  __register_frame_info_header_bases
>>>>+}
>>>
>>>You can't push new symbols into old versions.  These have to go into the
>>>version for the current gcc.
>>>
>>>>Index: libstdc++-v3/config/abi/pre/gnu.ver
>>>>
>>>==========================================================
>>>=========
>>>>--- libstdc++-v3/config/abi/pre/gnu.ver	(revision 226409)
>>>>+++ libstdc++-v3/config/abi/pre/gnu.ver	(working copy)
>>>>@@ -1909,6 +1909,7 @@ CXXABI_1.3 {
>>>>      __gxx_personality_v0;
>>>>      __gxx_personality_sj0;
>>>>      __gxx_personality_seh0;
>>>>+    __gnu_compact_pr2;
>>>>      __dynamic_cast;
>>>>
>>>>      # *_type_info classes, ctor and dtor
>>>>Index: libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
>>>>
>>>==========================================================
>>>=========
>>>>--- libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
>>>	(revision 226409)
>>>>+++ libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
>>>	(working copy)
>>>>@@ -200,6 +200,7 @@ CXXABI_2.0 {
>>>>      __cxa_vec_new;
>>>>      __gxx_personality_v0;
>>>>      __gxx_personality_sj0;
>>>>+    __gnu_compact_pr2;
>>>>      __dynamic_cast;
>>>>
>>>>      # std::exception_ptr
>>>
>>>Likewise.
>>>
>>I'm getting ready to post the updates to this patch -- hopefully, I can still get it in GCC 6.0.
>>I'm not sure how to tell what the current CXXABI is for these two files.  Should it be CXXABI_2.0 for both of these?
>
>Jonathan, can you answer this question?

It's OK to add to CXXABI_2.0 for the gnu-versioned-namespace file,
when you configure libstdc++ to use the versioned namespaces you're
explicitly requesting no ABI compatibility, and everything gets the
same version.

For gnu.ver you need to create a new symbol version and add new
symbols in there. CXXABI_1.3.9 was present in GCC 5, so you need to
create CXXABI_1.3.10 for your new symbol.

CXXABI_1.3.10 {

    __gnu_compact_pr2;

} CXXABI_1.3.9;

^ permalink raw reply	[flat|nested] 14+ messages in thread

* RE: [RFA] Compact EH Patch
  2015-09-18 19:34 ` Richard Henderson
  2015-10-05 23:14   ` Moore, Catherine
  2015-11-25 17:13   ` Moore, Catherine
@ 2015-12-13 22:12   ` Moore, Catherine
  2 siblings, 0 replies; 14+ messages in thread
From: Moore, Catherine @ 2015-12-13 22:12 UTC (permalink / raw)
  To: Richard Henderson, gcc-patches; +Cc: jason, Matthew Fortune

[-- Attachment #1: Type: text/plain, Size: 9324 bytes --]

I've now updated the patch.  I'd like to get this in GCC 6.0; please let me know if that is possible.
I've also updated the spec (https://github.com/MentorEmbedded/cxx-abi/MIPSCompactEH.pdf).
 rth commented that there was no versioning info provision for the personality routines.  The now spec identifies a couple of bits in the header that can be used for that.  A couple of new unwind opcodes were also added.

With the exception of Matthew's comment about disabling Compact EH for those ABIs that don't support it, I have not addressed the MIPS-specific comments.  I will post a follow-on patch next week to take care of that.
There is also a problem with N64 ABI support that requires a follow-on binutils patch.  I have disabled Compact EH for N64 on Linux in this version of the patch.  I plan to re-enable it after binutils is updated.

Okay to commit?

Thanks,
Catherine


> -----Original Message-----
> From: Richard Henderson [mailto:rth@redhat.com]
> Sent: Friday, September 18, 2015 3:25 PM
> To: Moore, Catherine; gcc-patches@gcc.gnu.org
> Cc: jason@redhat.com; Matthew Fortune
> Subject: Re: [RFA] Compact EH Patch
> 
> > Index: libgcc/libgcc-std.ver.in
> >
> ==========================================================
> =========
> > --- libgcc/libgcc-std.ver.in	(revision 226409)
> > +++ libgcc/libgcc-std.ver.in	(working copy)
> > @@ -1918,6 +1918,7 @@ GCC_4.6.0 {
> >    __morestack_current_segment
> >    __morestack_initial_sp
> >    __splitstack_find
> > +  _Unwind_GetEhEncoding
> >  }
> >
> >  %inherit GCC_4.7.0 GCC_4.6.0
> > @@ -1938,3 +1939,8 @@ GCC_4.7.0 {
> >  %inherit GCC_4.8.0 GCC_4.7.0
> >  GCC_4.8.0 {
> >  }
> > +
> > +%inherit GCC_4.8.0 GCC_4.7.0
> > +GCC_4.8.0 {
> > +  __register_frame_info_header_bases
> > +}
> 
> You can't push new symbols into old versions.  These have to go into the
> version for the current gcc.

Done.
> 
> > Index: libstdc++-v3/config/abi/pre/gnu.ver
> >
> ==========================================================
> =========
> > --- libstdc++-v3/config/abi/pre/gnu.ver	(revision 226409)
> > +++ libstdc++-v3/config/abi/pre/gnu.ver	(working copy)
> > @@ -1909,6 +1909,7 @@ CXXABI_1.3 {
> >      __gxx_personality_v0;
> >      __gxx_personality_sj0;
> >      __gxx_personality_seh0;
> > +    __gnu_compact_pr2;
> >      __dynamic_cast;
> >
> >      # *_type_info classes, ctor and dtor

Done.

> > Index: libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
> >
> ==========================================================
> =========
> > --- libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
> 	(revision 226409)
> > +++ libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
> 	(working copy)
> > @@ -200,6 +200,7 @@ CXXABI_2.0 {
> >      __cxa_vec_new;
> >      __gxx_personality_v0;
> >      __gxx_personality_sj0;
> > +    __gnu_compact_pr2;
> >      __dynamic_cast;
> >
> >      # std::exception_ptr
> 
Jonathan says that CXXABI_2.0 is correct here.
> 
> > +  if (data.type != CET_not_found)
> > +    return data.type;
> > +
> > +  return CET_not_found;
> 
> Return data.type unconditionally.

Done.
> 
> > +++ libgcc/unwind-pe.h	(working copy)
> > @@ -44,6 +44,7 @@
> >  #define DW_EH_PE_udata2         0x02
> >  #define DW_EH_PE_udata4         0x03
> >  #define DW_EH_PE_udata8         0x04
> > +#define DW_EH_PE_udata1         0x05
> >  #define DW_EH_PE_sleb128        0x09
> >  #define DW_EH_PE_sdata2         0x0A
> >  #define DW_EH_PE_sdata4         0x0B
> 
> If we're going to add udata1, we might as well add sdata1 for consistency.

Done.
> 
> > @@ -184,6 +187,7 @@ read_encoded_value_with_base (unsigned char
> encodi
> >    union unaligned
> >      {
> >        void *ptr;
> > +      unsigned u1 __attribute__ ((mode (QI)));
> >        unsigned u2 __attribute__ ((mode (HI)));
> >        unsigned u4 __attribute__ ((mode (SI)));
> >        unsigned u8 __attribute__ ((mode (DI)));
> 
> This is silly.  Access to a single byte never requires alignment help from the
> compiler...
> 
> > +	case DW_EH_PE_udata1:
> > +	  result = u->u1;
> > +	  p += 1;
> > +	  break;
> 
>     result = *p;

Done.

> 
> > +  read_encoded_value_with_base (DW_EH_PE_absptr |
> DW_EH_PE_udata4, 0,
> > +				hdr + 4, &nrec);
> 
> Err... that encoding type makes no sense: absptr is "pointer size".  Combining
> that with an explicit size, like udata4, means that udata4 wins.  So this is the
> same as simply writing DW_EH_PE_udata4.
> 
> At which point you're loading a 4 byte unsigned quantity from an aligned
> address.  You might as well use *(uint32_t *)(hdr + 4).
> 

Done.
> > +__register_frame_info_header_bases (const void *begin, struct object
> *ob,
> > +				    void *tbase, void *dbase)
> > +{
> > +  /* Only register compact EH frame headers.  */
> > +  if (begin == NULL || *(const char *) begin != 2)
> > +    return;
> 
> Check for no entries before registering?

Done.
> 
> > +extern char __GNU_EH_FRAME_HDR[] TARGET_ATTRIBUTE_WEAK;
> 
> Don't you have some guaranteed alignment for this table?
> That perhaps ought to be seen in either the type or an attribute.
> 
> +      if (op < 0x40)
> +      else if (op < 0x48)
> +      else if (op < 0x50)
> +      else if (op < 0x58)
> +      else if (op == 0x58)
> +      else if (op == 0x59)
> +      else if (op == 0x5a)
> +      else if (op == 0x5b)
> +      else if (op == 0x5c)
> +      else if (op == 0x5d)
> +      else if (op == 0x5e)
> +      else if (op == 0x5f)
> +      else if (op >= 0x60 && op < 0x6c)
> 
> Better as a switch statement surely, using the gcc case x ... y: extension.

Done.
> 
> > +static _Unwind_Reason_Code
> > +uw_frame_state_compact (struct _Unwind_Context *context,
> > +			_Unwind_FrameState *fs,
> > +			enum compact_entry_type entry_type,
> > +		       	struct compact_eh_bases *bases) {
> > +  const unsigned char *p;
> > +  unsigned int pr_index;
> > +  _Unwind_Ptr personality;
> > +  unsigned char buf[4];
> > +  _Unwind_Reason_Code rc;
> > +
> > +  p = bases->entry;
> > +  pr_index = *(p++);
> > +  switch (pr_index) {
> > +  case 0:
> > +      p = read_encoded_value (context, bases->eh_encoding, p,
> &personality);
> > +      fs->personality = (_Unwind_Personality_Fn) personality;
> > +      break;
> > +  case 1:
> > +      fs->personality = __gnu_compact_pr1;
> > +      break;
> > +  case 2:
> > +      fs->personality = __gnu_compact_pr2;
> > +      break;
> > +  default:
> > +      fs->personality = NULL;
> > +  }
> 
> This is the aspect of this spec about which I am least keen.  The existing
> method whereby the personality function is explicit in each object file means
> that we've got automatic version control on data that is private to the object
> file.
> 
> That is, if we should ever change the format of the LSDA -- as in fact you are
> doing here -- then all we need do is change __gcc_personality_v0 to
> __gcc_personality_v1, and all is well.  One can mix and match object files
> from different compiler versions and all that is required for correctness is
> that the runtime libraries must continue to provide all previous versions.
> 
> What you're doing here doesn't allow the format of index {1,2,3} to *ever*
> change.  You've fixed it forever, barring abandoning those indices and always
> using index 0.
> 
> I know that the ARM EH format made exactly the same mistake, but let's see
> if we can find some way of not replicating it, eh?


This has been addressed with a specification update to allocate some additional bits for versioning.
> 
> 
> > @@ -206,4 +206,14 @@ _Unwind_SetIP (struct _Unwind_Context
> *context, _U
> >    return __libunwind_Unwind_SetIP (context, val);  }  symver
> > (_Unwind_SetIP, GCC_3.0);
> > +
> > +extern unsigned char __libunwind_Unwind_GetEhEncoding
> > +  (struct _Unwind_Context *);
> > +
> > +unsigned char
> > +_Unwind_GetEhEncoding (struct _Unwind_Context *context) {
> > +  return __libunwind_Unwind_GetEhEncoding (context, val); } symver
> > +(_Unwind_GetEhEncoding, GCC_3.0);
> >  #endif
> 
> There is no such __libunwind symbol, is there?

Removed.
> 
> > +mcompact-eh
> > +Target Var(TARGET_COMPACT_EH) Init(1) Use compact exception
> unwinding
> > +tables.
> 
> This ought to be automatically disabled with -fasynchronous-unwind-tables.

We don't disable it, but we emit dwarf-based unwind information.  The compact header knows that it's DWARF and processes it accordingly.
See Section 10 in the spec -- we have compact-inline, compact-outline and legacy-style unwind info.

> 
> >  	  /* "Save" $sp in itself so we don't use the fake CFA.
> >  	     This is: DW_CFA_val_expression r29, { DW_OP_reg29 }.  */
> > -	  fprintf (asm_out_file, "\t.cfi_escape 0x16,29,1,0x6d\n");
> > +	  if (dwarf2out_do_frame ()
> > +	      && (flag_unwind_tables || flag_exceptions))
> > +	    /* "Save" $sp in itself so we don't use the fake CFA.
> > +	       This is: DW_CFA_val_expression r29, { DW_OP_reg29 }.  */
> > +	    fprintf (asm_out_file, "\t.cfi_fde_data 0x5b\n");
> > +	  else
> > +	    fprintf (asm_out_file, "\t.cfi_escape 0x16,29,1,0x6d\n");
> 
> The commentary appears to be messed up?

Fixed.



[-- Attachment #2: compact-eh.cl --]
[-- Type: application/octet-stream, Size: 6160 bytes --]

2015-12-13  Catherine Moore  <clm@codesourcery.com>
	    Bernd Schmidt    <bernds@codesourcery.com>
	    Paul Brook  <paul@codesourcery.com>

	libstdc++-v3/
	* libsupc++/eh_personality.cc (compact_get_ttype_entry): New.
	(check_compact_exception_spec): New.
	(__cxa_call_unexpected): Handle Compact EH.
	* libsupc++/Makefile.am (eh_compact_pr.cc): Add to sources.
	* libsupc++/Makefile.in: Regenerate.
	* libsupc++/eh_compact_pr.cc: New file.
	* libsupc++/unwind-cxx.h (__gnu_compact_pr2): Declare.
	* config/abi/pre/gnu.ver (__gnu_compact_pr2): Add to CXXABI_1.3.10.
	* config/abi/pre/gnu-versioned-namespace.ver (__gnu_compact_pr2):
	Add to CXXABI_2.0.

	gcc/
	* config/mips/sde.h (MIPS_COMPACT_EH_PCREL): Define.
	* config/mips/mips-protos.h (mips_fixup_cfi_sections): Declare.
	* config/mips/linux.h (MD_HAVE_COMPACT_EH): Define.
	(ABI_HAS_COMPACT_EH_SUPPORT): Define.
	* config/mips/elf.h (MD_HAVE_COMPACT_EH): Define.
	* config/mips/mips.opt (mcompact-eh): New option.
	* config/mips/mips.c: Include except.h and debug.h.
	(done_cfi_sections): New.
	(mips_fixup_cfi_sections): New.
	(mips16_build_call_stub): Emit Compact EH cfi data.
	(mips_asm_init_sections): New.
	(mips_in_small_data_p): Add Compact EH support.
	(mips_file_start): Likewise.
	(add_fp_ra_push): New.
	(mips_fdedata_cfaadjust): New.
	(mips_cfi_endproc): New.
	(TARGET_ASM_INIT_SECTIONS): New.
	(TARGET_OUTPUT_CFI_ENDPROC): Define.
	* config/mips/mips.h (TARGET_CPU_CPP_BUILTINS): Add __GNU_COMPACT_EH__.
	(ASM_DECLARE_FUNCTION_NAME): Define.
	(MIPS_COMPACT_EH_PCREL): Define.
	(MIPS_COMPACT_EH_ENCODING): Define.
	(ASM_PREFERRED_EH_DATA): Redefine.
	(ASM_MAYBE_OUTPUT_ENCODED_ADDR_RTX): Define.
	(MIPS_EH_ENCODING): Define.
	(md_unwind_compact_opcode_finish): Define.
	(CRT_GET_RFIB_DATA): Define.
	* doc/tm.texi: Regenerate.
	* doc/tm.texi.in (TARGET_OUTPUT_CFI_ENDPROC): New hook.
	* doc/invoke.texi (mcompact-eh): Document.
	* dwarf2out.c (FUNC_BEGIN_SWITCH_LABEL): Define.
	(output_call_frame_info): Add support for Compact EH.
	(dwarf2out_do_cfi_startproc):  Likewise.
	(dwarf2out_end_epilogue): Likewise.
	(dwarf2out_switch_text_section): Likewise.
	(except.h): Now included.
	* except.c (using_compact_pr): New.
	(compact_pr_id): New.
	(read_sleb128): New.
	(push_uleb128): Make external.
	(func_begin_lab, switch_begin_lab): New.
	(region_begin_lab, region_end_lab): New.
	(chain_d, chain_ref, chains): Declare.
	(compact_info_d, compact_info_ref, compact_info): Declare.
	(dummy_info_d, dummy_info_ref, dummy_info): Declare.
	(compact_create_region_start_label): New.
	(compact_create_region_end_label): New.
	(compact_emit_region_header): New.
	(compact_insert_nothrow): New.
	(compact_insert_continue): New.
	(compact_insert_cleanup): New.
	(compact_link_to_dummy): New.
	(compact_emit_dummies): New.
	* targhooks.c (default_asm_output_cfi_endproc): New.
	* targhooks.h (default_asm_output_cfi_endproc): Declare.
	* final.c (rest_of_handle_final): Add argument
	to output_function_exception_table.
	* config.in (HAVE_GAS_EH_FRAME_ENTRY): Provide default.
	* configure.ac (HAVE_GAS_EH_FRAME_ENTRY): Check for assembler
	support.
	* configure: Regenerate.
	* expr.c (build_personality_function): Add Compact EH support.
	* gcc/testsuite/
	* g++.dg/eh/catch6.C: New test.
	* except.h (eh_compact_header_type): New enumeration.
	(compact_pr_id): Declare.
	(output_function_exception_table): Add argument.
	(push_uleb128): Declare.
	* dwarf2cfi.c (dwarf2out_do_cfi_asm): Compact EH support.
	* dwarf2asm.c (dw2_asm_output_comment): New.
	(eh_data_format_name): Compact EH support.
	(dw2_asm_output_compact_region_length): New.
	(dw2_asm_output_compact_landing_pad): New.
	(dw2_asm_output_compact_ac_pair_sleb128): New.
	* dwarf2asm.h (dw2_asm_output_comment): Declare.
	(dw2_asm_output_compact_region_length): Declare.
	(dw2_asm_output_compact_landing_pad): Declare.
	(dw2_asm_output_compact_ac_pair_sleb128): Declare.
	(GCC_DW_EH_PE_special): Define.
	* target.def (output_cfi_endproc): New hook.
	* defaults.h (TARGET_COMPACT_EH): Provide default.


	libgcc/
	* config.host (mips*-sde-elf*): Set md_unwind_header.
	* Makefile.in (LIB2ADDEH): Add unwind-compact.c.
	* unwind-dw2-fde-dip.c: Include unwind-compact.h.
	(_Unwind_Find_registered_Index): Add declaration.
	(unw_eh_callback_data): Use struct compact_eh_bases.  Add type.
	(base_from_cb_data): Use data->bases.
	(_Unwind_IteratePhdrCallback): Search compact frame headers.
	Use data->bases.  Set data->type.
	(_Unwind_Find_FDE): Rename...
	(_Unwind_Find_Index): To this.  Adjust for compact EH.
	* unwind-compact.c: New file.
	* unwind-compact.h: New file.
	* unwind-dw2-fde.c: Include unwind-compact.h.
	(__register_frame_info_1, __register_frame_info_header_bases): New
	functions.
	(__register_frame_info_bases): Use __register_frame_info_1.
	(search_object): Search compact unwind table headers.
	Set bases->entry.  Return entry type.
	(_Unwind_Find_FDE): Implement as wrapper around _Unwind_Find_Index.
	(_Unwind_Find_registered_Index): New function.
	* unwind-dw2-fde.h (object): Add flag for compact frame headers.
	(__register_frame_info_header_bases): Add prototype.
	(compact_eh_bases, compact_entry_type): New.
	(_Unwind_Find_Index): Add prototype.
	* crtstuff.c (__register_frame_info_header_bases): Declare
	(__GNU_EH_FRAME_HDR): Declare.
	(frame_dummy): Register eh_frame_hdr tables.
	* config/t-eh-dw2-dip (LIB2ADDEH): Add $(srcdir)/unwind-compact.c.
	* config/mips/linux-unwind.h: Include mips-unwind.h.
	* config/mips/mips-unwind.h: New file.
	* unwind-dw2.c (__gnu_compact_pr1, uw_frame_state_compact):
	New functions.
	(__gnu_compact_pr2): Use weak references.
	(uw_frame_state_for): Handle compact frame entries.
	* mips16.S: Add Compact EH support.
	* unwind-pe.h (DW_EH_PE_udata1, DW_EH_PE_sdata1): Define.
	(size_of_encoded_value, read_encoded_value_with_base): Handle
	DW_EH_PE_udata1.
	(unaligned): Add u1.
	* unwind-generic.h (_Unwind_GetEhEncoding): Declare.
	* libgcc-std.ver.in: Add _Unwind_GetEhEncoding and
	__register_frame_info_header_bases.

[-- Attachment #3: compact-eh.patch --]
[-- Type: application/octet-stream, Size: 130814 bytes --]

Index: libstdc++-v3/config/abi/pre/gnu.ver
===================================================================
--- libstdc++-v3/config/abi/pre/gnu.ver	(revision 231238)
+++ libstdc++-v3/config/abi/pre/gnu.ver	(working copy)
@@ -2109,6 +2109,12 @@ CXXABI_1.3.9 {
 
 } CXXABI_1.3.8;
 
+CXXABI_1.3.10 {
+
+    __gnu_compact_pr2;
+
+} CXXABI_1.3.9;
+
 # Symbols in the support library (libsupc++) supporting transactional memory.
 CXXABI_TM_1 {
 
Index: libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver
===================================================================
--- libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver	(revision 231238)
+++ libstdc++-v3/config/abi/pre/gnu-versioned-namespace.ver	(working copy)
@@ -200,6 +200,7 @@ CXXABI_2.0 {
     __cxa_vec_new;
     __gxx_personality_v0;
     __gxx_personality_sj0;
+    __gnu_compact_pr2;
     __dynamic_cast;
 
     # std::exception_ptr
Index: libstdc++-v3/libsupc++/Makefile.am
===================================================================
--- libstdc++-v3/libsupc++/Makefile.am	(revision 231238)
+++ libstdc++-v3/libsupc++/Makefile.am	(working copy)
@@ -66,6 +66,7 @@ sources = \
 	eh_aux_runtime.cc \
 	eh_call.cc \
 	eh_catch.cc \
+	eh_compact_pr.cc \
 	eh_exception.cc \
 	eh_globals.cc \
 	eh_personality.cc \
Index: libstdc++-v3/libsupc++/eh_personality.cc
===================================================================
--- libstdc++-v3/libsupc++/eh_personality.cc	(revision 231238)
+++ libstdc++-v3/libsupc++/eh_personality.cc	(working copy)
@@ -234,6 +234,63 @@ get_adjusted_ptr (const std::type_info *catch_type
   return false;
 }
 
+// Return an element from a type table.
+
+static const std::type_info *
+compact_get_ttype_entry (_Unwind_Ptr base,
+			 unsigned char eh_encoding,
+			 _Unwind_Ptr ttab_start,
+			 int aval)
+{
+  _Unwind_Ptr ptr;
+
+  aval = (aval -  1) * size_of_encoded_value (eh_encoding);
+  read_encoded_value_with_base (eh_encoding, base,
+				(const unsigned char *) ttab_start + aval,
+				&ptr);
+
+  return reinterpret_cast<const std::type_info *>(ptr);
+}
+
+// Return true if THROW_TYPE matches one of the exception spec entries.
+static bool
+check_compact_exception_spec (_throw_typet* throw_type, void* thrown_ptr,
+			      const unsigned char *xh_lsda,
+			      _Unwind_Sword xh_switch_value,
+			      _Unwind_Ptr base)
+
+{
+  _Unwind_Ptr padding_start, ttable_start;
+  const std::type_info* catch_type;
+  unsigned char encoding = xh_switch_value & 0xff;
+  unsigned char eh_offset = abs (xh_switch_value >> 8);
+  const unsigned char *p, *cs_start, *cs_end, *ehtable_start;
+  _Unwind_Ptr lsda_header;
+  _uleb128_t ehval, call_site_len, ehspecs_len;
+  
+
+  p = read_encoded_value (0, DW_EH_PE_udata1, xh_lsda, &lsda_header);
+  cs_start = read_uleb128 (p, &call_site_len);
+  cs_end = cs_start + call_site_len;
+  ehtable_start  = read_uleb128 (cs_end, &ehspecs_len);
+  padding_start = (_Unwind_Ptr) ehtable_start + ehspecs_len;
+  ttable_start = (padding_start + 4 - 1) & ~(4 - 1);
+
+  p = read_uleb128 (ehtable_start + eh_offset - 1, &ehval);
+
+  while (ehval != 0)
+    {
+      catch_type =
+	compact_get_ttype_entry (base, encoding, ttable_start, ehval);
+
+      if (get_adjusted_ptr (catch_type, throw_type, &thrown_ptr))
+	return true;
+
+      p = read_uleb128 (p, &ehval);
+    }
+  return false;
+}
+
 // Return true if THROW_TYPE matches one if the filter types.
 
 static bool
@@ -765,21 +822,38 @@ __cxa_call_unexpected (void *exc_obj_in)
       __cxa_exception *new_xh = globals->caughtExceptions;
       void *new_ptr = __get_object_from_ambiguous_exception (new_xh);
 
-      // We don't quite have enough stuff cached; re-parse the LSDA.
-      parse_lsda_header (0, xh_lsda, &info);
+      if (xh_switch_value > 0)
+	{
+	  _throw_typet *new_type = __get_exception_header_from_obj
+	    (new_ptr)->exceptionType;
+	  if (check_compact_exception_spec (new_type, new_ptr, xh_lsda,
+					    xh_switch_value, info.ttype_base))
+	    __throw_exception_again;
+	}
+      else
+	{
+	  // We don't quite have enough stuff cached; re-parse the LSDA.
+	  parse_lsda_header (0, xh_lsda, &info);
 
-      // If this new exception meets the exception spec, allow it.
-      if (check_exception_spec (&info, __get_exception_header_from_obj
-                                  (new_ptr)->exceptionType,
-				new_ptr, xh_switch_value))
-	{ __throw_exception_again; }
+	  // If this new exception meets the exception spec, allow it.
+	  if (check_exception_spec (&info, __get_exception_header_from_obj
+				    (new_ptr)->exceptionType, new_ptr,
+				    xh_switch_value))
+	     __throw_exception_again;
 
+	}
       // If the exception spec allows std::bad_exception, throw that.
       // We don't have a thrown object to compare against, but since
       // bad_exception doesn't have virtual bases, that's OK; just pass 0.
 #if __cpp_exceptions && __cpp_rtti
       const std::type_info &bad_exc = typeid (std::bad_exception);
-      if (check_exception_spec (&info, &bad_exc, 0, xh_switch_value))
+      if (xh_switch_value > 0)
+	{
+	  if (check_compact_exception_spec (&bad_exc, 0, xh_lsda,
+					    xh_switch_value, info.ttype_base))
+	    throw std::bad_exception();
+	}
+      else if (check_exception_spec (&info, &bad_exc, 0, xh_switch_value))
 	throw std::bad_exception();
 #endif   
 
Index: libstdc++-v3/libsupc++/Makefile.in
===================================================================
--- libstdc++-v3/libsupc++/Makefile.in	(revision 231238)
+++ libstdc++-v3/libsupc++/Makefile.in	(working copy)
@@ -118,9 +118,10 @@ am__objects_1 = array_type_info.lo atexit_arm.lo a
 	bad_alloc.lo bad_array_length.lo bad_array_new.lo bad_cast.lo \
 	bad_typeid.lo class_type_info.lo del_op.lo del_ops.lo \
 	del_opnt.lo del_opv.lo del_opvs.lo del_opvnt.lo dyncast.lo \
-	eh_alloc.lo eh_arm.lo eh_aux_runtime.lo eh_call.lo eh_catch.lo \
-	eh_exception.lo eh_globals.lo eh_personality.lo eh_ptr.lo \
-	eh_term_handler.lo eh_terminate.lo eh_tm.lo eh_throw.lo \
+	eh_alloc.lo eh_arm.lo eh_aux_runtime.lo eh_call.lo \
+	eh_catch.lo eh_compact_pr.lo \
+	eh_exception.lo eh_globals.lo eh_personality.lo \
+	eh_ptr.lo eh_term_handler.lo eh_terminate.lo eh_tm.lo eh_throw.lo \
 	eh_type.lo eh_unex_handler.lo enum_type_info.lo \
 	function_type_info.lo fundamental_type_info.lo guard.lo \
 	guard_error.lo hash_bytes.lo nested_exception.lo \
@@ -423,6 +424,7 @@ sources = \
 	eh_aux_runtime.cc \
 	eh_call.cc \
 	eh_catch.cc \
+	eh_compact_pr.cc \
 	eh_exception.cc \
 	eh_globals.cc \
 	eh_personality.cc \
Index: libstdc++-v3/libsupc++/eh_compact_pr.cc
===================================================================
--- libstdc++-v3/libsupc++/eh_compact_pr.cc	(revision 0)
+++ libstdc++-v3/libsupc++/eh_compact_pr.cc	(revision 0)
@@ -0,0 +1,682 @@
+#ifdef __GNU_COMPACT_EH__
+// -*- C++ -*- The GNU C++ compact exception personality routine
+// Copyright (C) 2012
+// 
+// Free Software Foundation, Inc.
+//
+// This file is part of GCC.
+//
+// GCC is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// GCC is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+//
+// Written by Catherine Moore <clm@codesourcery.com>
+
+#include <bits/c++config.h>
+#include <cstdlib>
+#include <bits/exception_defines.h>
+#include <cxxabi.h>
+#include "unwind-cxx.h"
+
+using namespace __cxxabiv1;
+
+#include "unwind-pe.h"
+
+\f
+enum exception_entry_type
+{
+  CATCH_TYPE,
+  CLEANUP_TYPE,
+  CONTINUE_UNWINDING_TYPE,
+  EH_SPEC_TYPE,
+  NOTHROW_TYPE,
+  UNKNOWN_TYPE,
+}; 
+
+#define LENGTH_MASK 1
+
+#define NOTHROW_REGION(x) (x & 0x1)
+#define CONTINUE_UNWINDING_REGION(x) (x == -1)
+#define DUMMY_ACTION_REGION(x) (x == 0)
+#define EXTRACT_ACTION_VALUE(x) (((x & 3) ^ 2) - 2)
+#define EXTRACT_CHAIN_VALUE(x) (x >> 2)
+#define ACTION_EXTENSION -2
+#define HAVE_EHSPECS(x) (x & 0x40)
+#define TTABLE_START(x) (x + 4 - 1) & ~(4 - 1);
+
+// Return an element from a type table.
+
+static const std::type_info *
+compact_get_ttype_entry (_Unwind_Ptr base,
+			 unsigned char eh_encoding,
+			 _Unwind_Ptr ttab_start,
+			 int aval)
+{
+  _Unwind_Ptr ptr;
+
+  aval = (aval -  1) * size_of_encoded_value (eh_encoding);
+  read_encoded_value_with_base (eh_encoding, base,
+				(const unsigned char *) ttab_start + aval, &ptr);
+
+  return reinterpret_cast<const std::type_info *>(ptr);
+}
+
+// Given the thrown type THROW_TYPE, pointer to a variable containing a
+// pointer to the exception object THROWN_PTR_P and a type CATCH_TYPE to
+// compare against, return whether or not there is a match and if so,
+// update *THROWN_PTR_P.
+
+static bool
+get_adjusted_ptr (const std::type_info *catch_type,
+		  const std::type_info *throw_type,
+		  void **thrown_ptr_p)
+{
+  void *thrown_ptr = *thrown_ptr_p;
+
+  // Pointer types need to adjust the actual pointer, not
+  // the pointer to pointer that is the exception object.
+  // This also has the effect of passing pointer types
+  // "by value" through the __cxa_begin_catch return value.
+  if (throw_type->__is_pointer_p ())
+    thrown_ptr = *(void **) thrown_ptr;
+
+  if (catch_type->__do_catch (throw_type, &thrown_ptr, 1))
+    {
+      *thrown_ptr_p = thrown_ptr;
+      return true;
+    }
+
+  return false;
+}
+
+// Save stage1 handler information in the exception object.
+
+static inline void
+save_caught_exception (struct _Unwind_Exception *ue_header,
+		       void *thrown_ptr,
+		       int handler_switch_value,
+		       const unsigned char *language_specific_data,
+		       _Unwind_Ptr landing_pad)
+{
+  __cxa_exception* xh = __get_exception_header_from_ue(ue_header);
+
+  xh->handlerSwitchValue = handler_switch_value;
+  xh->languageSpecificData = language_specific_data;
+  xh->adjustedPtr = thrown_ptr;
+  xh->catchTemp = landing_pad;
+}
+
+// Restore the catch handler information saved during phase1.
+
+static inline void
+restore_caught_exception (struct _Unwind_Exception *ue_header,
+			  int &handler_switch_value,
+			  const unsigned char *& language_specific_data,
+			  _Unwind_Ptr& landing_pad)
+{
+  __cxa_exception* xh = __get_exception_header_from_ue(ue_header);
+  handler_switch_value = xh->handlerSwitchValue;
+  language_specific_data = xh->languageSpecificData;
+  landing_pad = (_Unwind_Ptr) xh->catchTemp;
+}
+
+// PCS_PTR points to a variable holding a pointer to the start of a new
+// LSDA entry.  This function parses the entry and updates PCS_PTR to
+// point past its end.  The variables at *PREGION_LEN, *PLP_OFF, *PAV and
+// *PCV are filled with information about the data found: the region
+// length, the landing pad offset, the action value and the chain value.
+
+static inline enum exception_entry_type
+compact_parse_header (const unsigned char **pcs_ptr, _uleb128_t *pregion_len,
+		      int *pav, int *pcv, _sleb128_t *plp_off)
+{
+  const unsigned char *cs_ptr = *pcs_ptr;
+  _uleb128_t region_len;
+  enum exception_entry_type type = NOTHROW_TYPE;
+
+  cs_ptr = read_uleb128 (cs_ptr, pregion_len);
+
+  region_len = *pregion_len;
+
+  /* If nothrow, advance to next entry.  */
+  if (!NOTHROW_REGION (region_len))
+    {
+      _sleb128_t lp = 0;
+      /* Dummy action entries, don't have landing pads.  */
+      if (!DUMMY_ACTION_REGION (region_len))
+	{
+	  cs_ptr = read_sleb128 (cs_ptr, plp_off);
+	  lp = *plp_off;
+	}
+      /* If continue_unwinding, advance to next entry.  */
+      if (CONTINUE_UNWINDING_REGION (lp))
+	type = CONTINUE_UNWINDING_TYPE;
+      else
+	{
+	  int action_value, chain_value;
+	  _sleb128_t ac_pair;
+
+	  cs_ptr = read_sleb128 (cs_ptr, &ac_pair);
+	  action_value = EXTRACT_ACTION_VALUE (ac_pair);
+	  chain_value = EXTRACT_CHAIN_VALUE (ac_pair);
+	  if (action_value == ACTION_EXTENSION)
+	    {
+	      _sleb128_t ac_ext;
+	      cs_ptr = read_sleb128 (cs_ptr, &ac_ext);
+	      action_value = ac_ext;
+	    }
+
+	  if (action_value == 0)
+	    type = CLEANUP_TYPE;
+	  else if (action_value > 0)
+	    type = CATCH_TYPE;
+	  else
+	    type = EH_SPEC_TYPE;
+
+	  *pav = action_value;
+	  *pcv = chain_value;
+	}
+    }
+
+  *pcs_ptr = cs_ptr;
+  return type;
+}
+
+/* Given a region entry number, return a pointer to its call-site
+   table entry.  */
+
+static const unsigned char *
+compact_goto_region (const unsigned char *cs_ptr, int region_no,
+		     int chain_to_region)
+{
+  while (true)
+    {
+      _uleb128_t region_len;
+      int action_value, chain_value;
+      _sleb128_t lp_off;
+
+      region_no++;
+      if (region_no == chain_to_region)
+	break;
+
+      compact_parse_header (&cs_ptr, &region_len, &action_value,
+			    &chain_value, &lp_off);
+    }
+  return cs_ptr;
+}
+
+/* Return the next region number.  The next region is the current
+   region number less the chain_value.  No Throw regions are ignored.  */
+
+static int
+compact_backward_find_chain (const unsigned char *cs_start,
+			     int region_no, int chain_value)
+{
+  int looking_for = region_no - 1;
+  int looking_at = -1;
+  const unsigned char *cs_ptr = cs_start;
+
+  while (true)
+    {
+      _uleb128_t region_len;
+      int action_value, unused_chain_value;
+      _sleb128_t lp_off;
+      enum exception_entry_type type;
+
+      looking_at++;
+      type = compact_parse_header (&cs_ptr, &region_len, &action_value,
+				   &unused_chain_value, &lp_off);
+
+      if (looking_at == looking_for)
+	{
+	  if (type != NOTHROW_TYPE && type != CONTINUE_UNWINDING_TYPE)
+	    chain_value = chain_value + 1;
+
+	  if (chain_value == 0)
+	    break;
+
+	  looking_at = -1;
+	  looking_for--;
+	  cs_ptr = cs_start;
+	}
+    }
+  return looking_at;
+}
+
+/* Return the next region number.  chain_value is the number of
+   region entries in the call-site table to advance.  No Throw
+   regions are ignored and not included in the count.  */
+
+static int
+compact_forward_find_chain (const unsigned char *cs_ptr, int region_no,
+			    int chain_value)
+{
+  int valid_regions = 0;
+
+  while (true)
+    {
+      _uleb128_t region_len;
+      int action_value, unused_chain_value;
+      _sleb128_t lp_off;
+      enum exception_entry_type type;
+
+      region_no++;
+      type = compact_parse_header (&cs_ptr, &region_len, &action_value,
+				   &unused_chain_value, &lp_off);
+
+      if (type == NOTHROW_TYPE || type == CONTINUE_UNWINDING_TYPE)
+	continue;
+
+      valid_regions++;
+      if (valid_regions == chain_value)
+	return region_no;
+    }
+}
+
+/* Return the next region number in the chain.  */
+
+static int
+compact_find_chain_to_region (const unsigned char *cs_start,
+			      const unsigned char **cs_ptr,
+			      int *region_no, int chain_value)
+{
+  int chain_to_region;
+
+  if (chain_value < 0)
+    chain_to_region
+      = compact_backward_find_chain (cs_start, *region_no, chain_value);
+  else
+    chain_to_region
+      = compact_forward_find_chain (*cs_ptr, *region_no, chain_value);
+
+  return chain_to_region;
+}
+
+typedef const std::type_info _throw_typet;
+
+namespace __cxxabiv1
+{
+
+#pragma GCC visibility push(default)
+extern "C" _Unwind_Reason_Code
+__gnu_compact_pr2 (int version __attribute__ ((unused)),
+		   _Unwind_Action actions,
+		   _Unwind_Exception_Class exception_class,
+		   struct _Unwind_Exception *ue_header,
+		   struct _Unwind_Context *context)
+{
+  const unsigned char *cs_ptr, *language_specific_data;
+  const unsigned char *cs_start, *cs_end;
+  const unsigned char *padding_start;
+  const unsigned char *ehspecs_start;
+
+  _Unwind_Ptr ip, ip_start, ip_end;
+  _Unwind_Ptr lp_start, landing_pad;
+  _Unwind_Ptr ttable_start;
+  _Unwind_Ptr lsda;
+
+  _sleb128_t lp_off;
+  _uleb128_t region_len;
+  _uleb128_t call_site_len;
+  _uleb128_t ehspecs_len;
+
+  const std::type_info* catch_type;
+  int chain_value, action_value;
+  int region_no;
+  int handler_switch_value;
+  bool chaining = false;
+  int chain_to_region;
+  enum exception_entry_type region_type;
+  bool foreign_exception;
+  int ip_before_insn = 0;
+
+  void *thrown_ptr = 0;
+  _throw_typet *throw_type;
+  unsigned char eh_encoding = _Unwind_GetEhEncoding (context);
+  _Unwind_Ptr base = base_of_encoded_value (eh_encoding, context);
+  __cxa_exception *xh = __get_exception_header_from_ue(ue_header);
+
+  enum found_handler_type
+  {
+    found_nothing,
+    found_terminate,
+    found_cleanup,
+    found_handler,
+  } found_type = found_nothing;
+
+  language_specific_data = (const unsigned char *)
+    _Unwind_GetLanguageSpecificData (context);
+
+  // If no LSDA, then there are no handlers or cleanups.
+  if (!language_specific_data)
+    return _URC_CONTINUE_UNWIND;
+
+  /* Read the LSDA header.  */
+  cs_ptr =
+    read_encoded_value (0, DW_EH_PE_udata1, language_specific_data, &lsda);
+  /* Then the Call-site length.  */
+  cs_ptr = read_uleb128 (cs_ptr, &call_site_len);
+  cs_start = cs_ptr;
+  cs_end = cs_start + call_site_len;
+
+  /* Read the ehspecs if they are present and setup
+     a pointer to the type table entries.  */
+  if (HAVE_EHSPECS (lsda))
+    {
+      ehspecs_start = read_uleb128 (cs_start + call_site_len, &ehspecs_len);
+      padding_start = ehspecs_start + ehspecs_len;
+      ttable_start = TTABLE_START ((_Unwind_Ptr) padding_start);
+    }
+  else
+    ttable_start = TTABLE_START ((_Unwind_Ptr) cs_start + call_site_len);
+
+  foreign_exception = !__is_gxx_exception_class(exception_class);
+  // Shortcut for phase 2 found handler for domestic exception.
+  if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
+      && !foreign_exception)
+    {
+      restore_caught_exception (ue_header, handler_switch_value,
+				language_specific_data, landing_pad);
+      found_type = (landing_pad == 0 ? found_terminate : found_handler);
+      goto install_context;
+    }
+
+#ifdef _GLIBCXX_HAVE_GETIPINFO
+  ip = _Unwind_GetIPInfo (context, &ip_before_insn);
+#else
+  ip = _Unwind_GetIP (context);
+#endif
+  if (!ip_before_insn)
+    --ip;
+
+  ip_start = lp_start = _Unwind_GetRegionStart (context);
+  region_no = 0;
+
+  /* Examine each region entry, looking for
+     a matching ip address or chain value.  */
+  while (cs_ptr < cs_end)
+    {
+      region_type = compact_parse_header (&cs_ptr, &region_len, &action_value,
+					  &chain_value, &lp_off);
+
+      if (DUMMY_ACTION_REGION (region_len) && !chaining)
+	break;
+
+      region_len &= ~LENGTH_MASK;
+      lp_start += region_len;
+      ip_end = ip_start + region_len;
+
+      switch (region_type)
+	{
+	case NOTHROW_TYPE:
+	  break;
+	
+	case CONTINUE_UNWINDING_TYPE:
+	  if (ip >= ip_start && ip <= ip_end)
+	    return _URC_CONTINUE_UNWIND;
+	  break;
+
+	case CLEANUP_TYPE:
+	  if (chaining
+	      || (ip >= ip_start && ip <= ip_end))
+	    {
+	      found_type = found_cleanup;
+	      handler_switch_value = action_value;
+	      if (!chaining)
+	        landing_pad = lp_start + lp_off;
+
+	      /* For cleanups, walk the chain.  The cleanup is
+		 processed only if there are no other matches.  */
+
+	      if (chain_value == 0)
+		goto found_something;
+	      else
+		{
+		  chaining = true;
+		  chain_to_region = 
+		      compact_find_chain_to_region (cs_start, &cs_ptr,
+						    &region_no, chain_value);
+		}
+	    }
+	  break;
+
+	case CATCH_TYPE:
+	  if (!chaining
+	      && (ip < ip_start || ip > ip_end))
+	    break;
+
+#ifdef __GXX_RTTI
+	  // During forced unwinding, match a magic exception type.
+	  if (actions & _UA_FORCE_UNWIND)
+	    throw_type = &typeid(abi::__forced_unwind);
+
+	  // With a foreign exception class, there's no exception type.
+	  else if (foreign_exception)
+	    throw_type = &typeid(abi::__foreign_exception);
+	  else
+#endif
+	    {
+	      thrown_ptr = __get_object_from_ue (ue_header);
+	      throw_type = __get_exception_header_from_obj
+		(thrown_ptr)->exceptionType;
+	    }
+	  catch_type =
+	    compact_get_ttype_entry (base,
+				     eh_encoding, ttable_start, action_value);
+
+	  if (catch_type == 0
+	      || get_adjusted_ptr (catch_type, throw_type, &thrown_ptr))
+	    {
+	      found_type = found_handler;
+	      handler_switch_value = action_value;
+	      if (!chaining)
+		landing_pad = lp_start + lp_off;
+	      goto found_something;
+	    }
+
+	  /* Walk the chain if we didn't match.  */
+	  if (chain_value != 0)
+	    {
+	      if (!chaining)
+		landing_pad = lp_start + lp_off;
+	      chaining = true;
+	      chain_to_region = 
+		compact_find_chain_to_region (cs_start, &cs_ptr,
+					      &region_no, chain_value);
+	    }
+	  else
+	    // IP matched and not chaining.
+	    goto found_something;
+
+	  break;
+
+	case EH_SPEC_TYPE:
+#ifdef __GXX_RTTI
+	  // During forced unwinding, match a magic exception type.
+	  if (actions & _UA_FORCE_UNWIND)
+	    throw_type = &typeid(abi::__forced_unwind);
+
+	  // With a foreign exception class, there's no exception type.
+	  else if (foreign_exception)
+	    throw_type = &typeid(abi::__foreign_exception);
+
+	  else
+#endif
+	    {
+	      thrown_ptr = __get_object_from_ue (ue_header);
+	      throw_type = __get_exception_header_from_obj
+		(thrown_ptr)->exceptionType;
+	    }
+	  if (ip >= ip_start && ip <= ip_end)
+	    {
+	      if (!chaining)
+		landing_pad = lp_start + lp_off;
+
+	      /* An ehspec of zero is an immediate match.  */
+	      if (ehspecs_len == 0)
+		{
+		  found_type = found_handler;
+		  handler_switch_value = action_value;
+		  goto found_something;
+		}
+	      else if (throw_type
+		       && !(actions & _UA_FORCE_UNWIND)
+		       && !foreign_exception)
+		{
+		  const unsigned char *p;
+		  _uleb128_t ehval;
+
+		  bool matched_spec = false;
+		  int si = abs (action_value) - 1;
+		  p = read_uleb128 (ehspecs_start + si, &ehval);
+		  /* Read the ehspec entries until a match is found or the
+		     list is exhausted.  Process as a handler if no match
+		     is achieved.  */
+		  while (!matched_spec && ehval != 0)
+		    {
+	              catch_type = compact_get_ttype_entry (base, eh_encoding,
+							    ttable_start, ehval);
+
+		      if (get_adjusted_ptr (catch_type, throw_type, &thrown_ptr))
+			matched_spec = true;
+		      p = read_uleb128 (p, &ehval);
+		    }
+		  if (!matched_spec)
+		    {
+		      found_type = found_handler;
+		      handler_switch_value = action_value;
+		      goto found_something;
+		    }
+		}
+	      if (chain_value != 0)
+		{
+		  if (!chaining)
+		    landing_pad = lp_start + lp_off;
+		  chaining = true;
+		  chain_to_region = 
+		    compact_find_chain_to_region (cs_start, &cs_ptr,
+						  &region_no, chain_value);
+		}
+	      else
+		// IP matched and not chaining.
+		goto found_something;
+	    }
+	  break;
+
+	case UNKNOWN_TYPE:
+	  break;
+	}
+
+      /* If a chain was discovered, find the next region entry in the chain.  */
+      if (chaining && region_no != chain_to_region)
+	{
+	  if (chain_to_region < region_no)
+	    cs_ptr = compact_goto_region (cs_start, -1, chain_to_region);
+	  else
+	    cs_ptr = compact_goto_region (cs_ptr, region_no, chain_to_region);
+	  region_no = chain_to_region - 1;
+	}
+
+      ip_start = ip_end;
+      region_no++;
+    }
+	    
+  /* Each region has been examined without finding a match.  */
+  if (found_type == found_nothing)
+    {
+      found_type = found_terminate;
+      landing_pad = 0;
+    }
+
+found_something:
+
+  if (found_type == found_nothing)
+    return _URC_CONTINUE_UNWIND;
+
+  if (actions & _UA_SEARCH_PHASE)
+    {
+      if (found_type == found_cleanup)
+	return _URC_CONTINUE_UNWIND;
+
+      if (!foreign_exception)
+        {
+          save_caught_exception(ue_header, thrown_ptr, handler_switch_value,
+				language_specific_data, landing_pad);
+        }
+      return _URC_HANDLER_FOUND;
+    }
+
+install_context:
+  
+  // We can't use any of the cxa routines with foreign exceptions,
+  // because they all expect ue_header to be a struct __cxa_exception.
+  // So in that case, call terminate or unexpected directly.
+  if ((actions & _UA_FORCE_UNWIND)
+      || foreign_exception)
+    {
+      if (found_type == found_terminate)
+	std::terminate ();
+      else if (handler_switch_value < 0)
+	{
+	  __try 
+	    { std::unexpected (); } 
+	  __catch(...) 
+	    { std::terminate (); }
+	}
+    }
+  else
+    {
+      if (found_type == found_terminate)
+	__cxa_call_terminate(ue_header);
+
+      // Cache the base value for __cxa_call_unexpected, as we won't
+      // have an _Unwind_Context then.
+      if (handler_switch_value < 0)
+	{
+	  xh->catchTemp = base_of_encoded_value (eh_encoding, context);
+	  /* For the compact encoding, bits 0-7 of
+	     xh->handler_switch_value are used to store the
+	     eh_encoding.  Bits 8-31 of xh_handler_switch_value
+	     are used to store the offset from the beginning of
+             the LSDA to the type count for this unmatched exception
+             specification.  We store this as a positive number to 
+	     signal to __cxa_call_unexpected that the LSDA is in 
+             the compact format.  */
+
+	  xh->handlerSwitchValue = abs (xh->handlerSwitchValue) << 8;;
+	  xh->handlerSwitchValue = xh->handlerSwitchValue | eh_encoding;
+	  handler_switch_value = -1;
+
+	}  
+    }
+
+  /* For targets with pointers smaller than the word size, we must
+     extend the pointer, and this extension is target dependent.  */
+  _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
+		 __builtin_extend_pointer (ue_header));
+  /* handler_switch_value will always be -1 for an
+     unmatched exception spec header.  */
+  _Unwind_SetGR (context, __builtin_eh_return_data_regno (1),
+		 handler_switch_value);
+  _Unwind_SetIP (context, landing_pad);
+  return _URC_INSTALL_CONTEXT;
+}
+
+#pragma GCC visibility pop
+} // namespace __cxxabiv1
+#endif
Index: libstdc++-v3/libsupc++/unwind-cxx.h
===================================================================
--- libstdc++-v3/libsupc++/unwind-cxx.h	(revision 231238)
+++ libstdc++-v3/libsupc++/unwind-cxx.h	(working copy)
@@ -363,6 +363,11 @@ extern "C" _Unwind_Reason_Code __gxx_personality_v
      (int, _Unwind_Action, _Unwind_Exception_Class,
       struct _Unwind_Exception *, struct _Unwind_Context *);
 
+// GNU C++ compact eh personality routine.
+extern "C" _Unwind_Reason_Code __gnu_compact_pr2
+     (int, _Unwind_Action, _Unwind_Exception_Class,
+      struct _Unwind_Exception *, struct _Unwind_Context *);
+
 // GNU C++ sjlj personality routine, Version 0.
 extern "C" _Unwind_Reason_Code __gxx_personality_sj0
      (int, _Unwind_Action, _Unwind_Exception_Class,
Index: gcc/final.c
===================================================================
--- gcc/final.c	(revision 231238)
+++ gcc/final.c	(working copy)
@@ -4445,7 +4445,7 @@ rest_of_handle_final (void)
   /* The IA-64 ".handlerdata" directive must be issued before the ".endp"
      directive that closes the procedure descriptor.  Similarly, for x64 SEH.
      Otherwise it's not strictly necessary, but it doesn't hurt either.  */
-  output_function_exception_table (fnname);
+  output_function_exception_table (fnname, false);
 
   assemble_end_function (current_function_decl, fnname);
 
Index: gcc/testsuite/g++.dg/eh/catch6.C
===================================================================
--- gcc/testsuite/g++.dg/eh/catch6.C	(revision 0)
+++ gcc/testsuite/g++.dg/eh/catch6.C	(revision 0)
@@ -0,0 +1,53 @@
+using namespace std;
+
+extern "C" void abort ();
+
+class except1 {
+public:
+    explicit except1 (int _i) {}
+};
+
+class except2 {
+public:
+    explicit except2 (int _i) {}
+};
+
+void
+bar (int i)
+{
+  if (i & 1)
+    throw except1 (i);
+  throw except2 (i);
+}
+
+void
+f1 (int i)
+{
+  try { bar (i); }
+  catch (const except1& ex) { ; }
+}
+
+void
+f2 (int i)
+{
+  try { f1 (i); }
+  catch (const except2& ex) { ;; }
+}
+
+void
+foo (int i)
+{
+  try { f2 (i); }
+  catch (...)
+    {
+      abort ();
+    }
+}
+
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 50; i++)
+    foo (i);
+}
Index: gcc/testsuite/g++.old-deja/old-deja.exp
===================================================================
--- gcc/testsuite/g++.old-deja/old-deja.exp	(revision 231238)
+++ gcc/testsuite/g++.old-deja/old-deja.exp	(working copy)
@@ -28,7 +28,7 @@ dg-init
 
 # Gather a list of all tests, with the exception of those in directories
 # that are handled specially.
-set tests [lsort [find $srcdir/$subdir *.C]]
+set tests [lsort [find $srcdir/$subdir g++.mike/*.C]]
 
 # Main loop.
 g++-dg-runtest $tests "" $DEFAULT_CXXFLAGS
Index: gcc/dwarf2out.c
===================================================================
--- gcc/dwarf2out.c	(revision 231238)
+++ gcc/dwarf2out.c	(working copy)
@@ -91,6 +91,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-dfa.h"
 #include "gdb/gdb-index.h"
 #include "rtl-iter.h"
+#include "except.h"
 
 static void dwarf2out_source_line (unsigned int, const char *, int, bool);
 static rtx_insn *last_var_location_insn;
@@ -285,6 +286,10 @@ static GTY(()) rtx current_unit_personality;
 #define FUNC_BEGIN_LABEL	"LFB"
 #endif
 
+#ifndef FUNC_BEGIN_SWITCH_LABEL
+#define FUNC_BEGIN_SWITCH_LABEL	"LFCB"
+#endif
+
 #ifndef FUNC_END_LABEL
 #define FUNC_END_LABEL		"LFE"
 #endif
@@ -908,7 +913,8 @@ output_call_frame_info (int for_eh)
       dw2_asm_output_data_uleb128 (augmentation_size, "Augmentation size");
       if (personality)
 	{
-	  dw2_asm_output_data (1, per_encoding, "Personality (%s)",
+	  dw2_asm_output_data (1, per_encoding & ~GCC_DW_EH_PE_special,
+			       "Personality (%s)",
 			       eh_data_format_name (per_encoding));
 	  dw2_asm_output_encoded_addr_rtx (per_encoding,
 					   personality,
@@ -916,11 +922,13 @@ output_call_frame_info (int for_eh)
 	}
 
       if (any_lsda_needed)
-	dw2_asm_output_data (1, lsda_encoding, "LSDA Encoding (%s)",
+	dw2_asm_output_data (1, lsda_encoding & ~GCC_DW_EH_PE_special,
+			     "LSDA Encoding (%s)",
 			     eh_data_format_name (lsda_encoding));
 
       if (fde_encoding != DW_EH_PE_absptr)
-	dw2_asm_output_data (1, fde_encoding, "FDE Encoding (%s)",
+	dw2_asm_output_data (1, fde_encoding & ~GCC_DW_EH_PE_special,
+			     "FDE Encoding (%s)",
 			     eh_data_format_name (fde_encoding));
     }
 
@@ -961,11 +969,15 @@ dwarf2out_do_cfi_startproc (bool second)
 {
   int enc;
   rtx ref;
-  rtx personality = get_personality_function (current_function_decl);
+  rtx personality;
 
   fprintf (asm_out_file, "\t.cfi_startproc\n");
 
-  if (personality)
+  personality = get_personality_function (current_function_decl);
+  enc = compact_pr_id (personality);
+  if (enc != 0)
+    fprintf (asm_out_file, "\t.cfi_personality_id 0x%x\n", enc);
+  else if (personality)
     {
       enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2, /*global=*/1);
       ref = personality;
@@ -977,7 +989,8 @@ dwarf2out_do_cfi_startproc (bool second)
       if (enc & DW_EH_PE_indirect)
 	ref = dw2_force_const_mem (ref, true);
 
-      fprintf (asm_out_file, "\t.cfi_personality %#x,", enc);
+      fprintf (asm_out_file, "\t.cfi_personality %#x,",
+	       enc & ~GCC_DW_EH_PE_special);
       output_addr_const (asm_out_file, ref);
       fputc ('\n', asm_out_file);
     }
@@ -995,7 +1008,8 @@ dwarf2out_do_cfi_startproc (bool second)
       if (enc & DW_EH_PE_indirect)
 	ref = dw2_force_const_mem (ref, true);
 
-      fprintf (asm_out_file, "\t.cfi_lsda %#x,", enc);
+      fprintf (asm_out_file, "\t.cfi_lsda %#x,",
+	       enc & ~GCC_DW_EH_PE_special);
       output_addr_const (asm_out_file, ref);
       fputc ('\n', asm_out_file);
     }
@@ -1158,7 +1172,7 @@ dwarf2out_end_epilogue (unsigned int line ATTRIBUT
   cached_next_real_insn = NULL;
 
   if (dwarf2out_do_cfi_asm ())
-    fprintf (asm_out_file, "\t.cfi_endproc\n");
+    targetm.asm_out.output_cfi_endproc ();
 
   /* Output a label to mark the endpoint of the code generated for this
      function.  */
@@ -1226,8 +1240,10 @@ dwarf2out_switch_text_section (void)
     dwarf2out_note_section_used ();
 
   if (dwarf2out_do_cfi_asm ())
-    fprintf (asm_out_file, "\t.cfi_endproc\n");
+    targetm.asm_out.output_cfi_endproc ();
 
+  output_function_exception_table (NULL, true);
+
   /* Now do the real section switch.  */
   sect = current_function_section ();
   switch_to_section (sect);
@@ -1236,6 +1252,9 @@ dwarf2out_switch_text_section (void)
     = (sect == text_section
        || (cold_text_section && sect == cold_text_section));
 
+  ASM_OUTPUT_DEBUG_LABEL (asm_out_file, FUNC_BEGIN_SWITCH_LABEL,
+			  current_function_funcdef_no);
+
   if (dwarf2out_do_cfi_asm ())
     dwarf2out_do_cfi_startproc (true);
 
Index: gcc/config.in
===================================================================
--- gcc/config.in	(revision 231238)
+++ gcc/config.in	(working copy)
@@ -1189,6 +1189,13 @@
 #endif
 
 
+/* Define if your assembler supports generation of .eh_frame_entry from CFI
+   directives. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_GAS_EH_FRAME_ENTRY
+#endif
+
+
 /* Define if your assembler supports @gnu_unique_object. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_GAS_GNU_UNIQUE_OBJECT
Index: gcc/configure.ac
===================================================================
--- gcc/configure.ac	(revision 231238)
+++ gcc/configure.ac	(working copy)
@@ -5094,6 +5094,13 @@ AC_DEFINE_UNQUOTED(LD_COMPRESS_DEBUG_OPTION, "$gcc
 [Define to the linker option to enable compressed debug sections.])
 AC_MSG_RESULT($gcc_cv_ld_compress_debug)
 
+gcc_GAS_CHECK_FEATURE([CFI .eh_frame_entry],
+ gcc_cv_as_eh_frame_entry,
+ ,,
+[	.cfi_sections .eh_frame_entry ],,
+[AC_DEFINE(HAVE_GAS_EH_FRAME_ENTRY, 1,
+[Define if your assembler supports generation of .eh_frame_entry from CFI directives.])])
+
 # --------
 # UNSORTED
 # --------
Index: gcc/expr.c
===================================================================
--- gcc/expr.c	(revision 231238)
+++ gcc/expr.c	(working copy)
@@ -11503,7 +11503,6 @@ const_vector_from_tree (tree exp)
 tree
 build_personality_function (const char *lang)
 {
-  const char *unwind_and_version;
   tree decl, type;
   char *name;
 
@@ -11512,21 +11511,23 @@ build_personality_function (const char *lang)
     case UI_NONE:
       return NULL;
     case UI_SJLJ:
-      unwind_and_version = "_sj0";
+      name = ACONCAT (("__", lang, "_personality_sj0", NULL));
       break;
     case UI_DWARF2:
     case UI_TARGET:
-      unwind_and_version = "_v0";
+      if (TARGET_COMPACT_EH
+	  && (strcmp (lang_hooks.name, "GNU C++") == 0))
+	name = ACONCAT (("__gnu_compact_pr2", NULL));
+      else
+	name = ACONCAT (("__", lang, "_personality_v0", NULL));
       break;
     case UI_SEH:
-      unwind_and_version = "_seh0";
+      name = ACONCAT (("__", lang, "_personality_seh0", NULL));
       break;
     default:
       gcc_unreachable ();
     }
 
-  name = ACONCAT (("__", lang, "_personality", unwind_and_version, NULL));
-
   type = build_function_type_list (integer_type_node, integer_type_node,
 				   long_long_unsigned_type_node,
 				   ptr_type_node, ptr_type_node, NULL_TREE);
Index: gcc/except.c
===================================================================
--- gcc/except.c	(revision 231238)
+++ gcc/except.c	(working copy)
@@ -212,7 +212,6 @@ static void dw2_build_landing_pads (void);
 static int collect_one_action_chain (action_hash_type *, eh_region);
 static int add_call_site (rtx, int, int);
 
-static void push_uleb128 (vec<uchar, va_gc> **, unsigned int);
 static void push_sleb128 (vec<uchar, va_gc> **, int);
 #ifndef HAVE_AS_LEB128
 static int dw2_size_of_call_site_table (int);
@@ -220,7 +219,37 @@ static int sjlj_size_of_call_site_table (void);
 #endif
 static void dw2_output_call_site_table (int, int);
 static void sjlj_output_call_site_table (void);
+static const unsigned char *read_sleb128 (const unsigned char *p,
+                                          HOST_WIDE_INT *val);
 
+
+/* Emit the compact LSDA tables if using a compact personality
+   routine.  We avoid a language-specific check to cover the LTO
+   case where the language is "GNU GIMPLE".  */
+static bool
+using_compact_pr (void)
+{
+  rtx personality = get_personality_function (current_function_decl);
+
+  if (personality
+      && strncmp (XSTR (personality, 0), "__gnu_compact", 13) == 0)
+    return true;
+
+  return false;
+}
+
+/* Return the id to be used with .cfi_personality_id, or 0 to use
+   .cfi_personality.  */
+int
+compact_pr_id (rtx personality)
+{
+  if (!flag_asynchronous_unwind_tables
+      && personality
+      && strncmp (XSTR (personality, 0), "__gnu_compact_pr", 16) == 0)
+    return XSTR (personality, 0)[16] - '0';
+
+  return 0;
+}
 \f
 void
 init_eh (void)
@@ -2662,7 +2691,7 @@ make_pass_convert_to_eh_region_ranges (gcc::contex
   return new pass_convert_to_eh_region_ranges (ctxt);
 }
 \f
-static void
+void
 push_uleb128 (vec<uchar, va_gc> **data_area, unsigned int value)
 {
   do
@@ -2695,6 +2724,7 @@ push_sleb128 (vec<uchar, va_gc> **data_area, int v
   while (more);
 }
 
+
 \f
 #ifndef HAVE_AS_LEB128
 static int
@@ -2917,7 +2947,785 @@ output_ttype (tree type, int tt_format, int tt_for
     dw2_asm_output_encoded_addr_rtx (tt_format, value, is_public, NULL);
 }
 
+static const unsigned char *
+read_sleb128 (const unsigned char *p, HOST_WIDE_INT *val)
+{
+  unsigned int shift = 0;
+  unsigned char byte;
+  HOST_WIDE_INT result;
+
+  result = 0;
+  do
+    {
+      byte = *p++;
+      result |= ((HOST_WIDE_INT) byte & 0x7f) << shift;
+      shift += 7;
+    }
+  while (byte & 0x80);
+
+  /* Sign-extend a negative value.  */
+  if (shift < 8 * sizeof(result) && (byte & 0x40) != 0)
+    result |= -(((unsigned HOST_WIDE_INT)1L) << shift);
+
+  *val = (HOST_WIDE_INT) result;
+  return p;
+}
+
+static const char func_begin_lab[] = "LFB";
+static const char switch_begin_lab[] = "LFCB";
+static const char region_begin_lab[] = "LEHB";
+static const char region_end_lab[] = "LEHE";
+
+/* Structure to track the action chain for each region.  */
+struct GTY (()) chain_d
+{
+  int dwarf_regno;
+  int action_ndx;
+  struct chain_d *next;
+};
+
+typedef struct chain_d *chain_ref;
+static GTY (()) vec<chain_ref, va_gc> *chains;
+
+/* Structure used to store information about each region.  */
+struct GTY (()) compact_info_d
+{
+  /*  Region type.  */
+  enum eh_compact_header_type region_type;
+
+  /* Corresponding dwarf region number, if any.  */
+  int dwarf_region_no;
+
+  /* If not a dwarf region, the previous dwarf region number.  */
+  int prev_dwarf_region_no;
+
+  /* If not a dwarf region, the next dwarf region number.  */
+  int next_dwarf_region_no;
+
+  /* For action entries, the index into the action table.  */
+  int first_action_ndx;
+
+  /* For action entries, the next index into the action table.  */
+  int next_action_ndx;
+
+  /* The landing pad.  */
+  rtx landing_pad;
+
+  /* The entry in the action table, pointed to by the action index.  */
+  int action_value;
+
+  /* Non-zero, if this entry chains.  The chain-value is relative to
+     the current entry and excludes nothrow regions.  */
+  int chain_value;
+
+  /* True if this entry terminates a chain.  */
+  bool end_of_chain;
+
+  /* The label prefix to used to mark the beginning of this region.   */
+  const char *begin_lab_prefix;
+
+  /* The label prefix used to mark the end of this region.   */
+  const char *end_lab_prefix;
+
+  /* The label number denoting the beginning of the region.  */
+  int begin_lab_no;
+
+  /* The label number denoting the end of the region.  */
+  int end_lab_no;
+
+  /* True if the next item in the chain is a dummy action region.  */
+  bool use_dummy;
+
+  /* The region number of the dummy action.  */
+  int dummy_index;
+};
+
+typedef struct compact_info_d *compact_info_ref;
+static GTY (()) vec<compact_info_ref, va_gc> *compact_info;
+
+/* Struct used to track dummy action regions.  */
+
+struct GTY (()) dummy_info_d
+{
+  /* The dwarf region number that caused this dummy to be created.  */
+  int dwarf_region_no;
+
+  /* Index into the action table.  */
+  int action_ndx;
+
+  /* The next index into the action table.  */
+  int next_action_ndx;
+
+  /* The entry in the action table, pointed to by the action index.  */
+  int action_value;
+
+  /* Non-zero, if this entry chains.  The chain-value is relative to
+     the current entry and excludes nothrow regions.  */
+  int chain_value;
+
+  /* True if this entry terminates a chain.  */
+  bool end_of_chain;
+};
+
+typedef struct dummy_info_d *dummy_info_ref;
+static GTY (()) vec<dummy_info_ref, va_gc> *dummy_info;
+
+/* Create the region start label.  */
+
 static void
+compact_create_region_start_label (compact_info_d *ci, char *region_start)
+{
+  switch (ci->region_type)
+    {
+    case ECHT_DUMMY_ACTION_CHAIN:
+      break;
+
+    default:
+      ASM_GENERATE_INTERNAL_LABEL (region_start, ci->begin_lab_prefix,
+				   ci->begin_lab_no);
+    }
+}
+
+/* Create the region end label.  */
+
+static void
+compact_create_region_end_label (compact_info_d *ci, char *region_end)
+{
+  switch (ci->region_type)
+    {
+    case ECHT_DUMMY_ACTION_CHAIN:
+      break;
+
+    default:
+      ASM_GENERATE_INTERNAL_LABEL (region_end,
+				   ci->end_lab_prefix, ci->end_lab_no);
+      break;
+    }
+}
+
+/* Emit the region header for this entry.  */
+static void
+compact_emit_region_header (int regno, compact_info_d *ci)
+{
+  char region_start[32];
+  char region_end[32];
+  char landing_pad[32];
+
+  compact_create_region_start_label (ci, &region_start[0]);
+  compact_create_region_end_label (ci, &region_end[0]);
+
+  if (ci->landing_pad != NULL_RTX
+      && ci->landing_pad != constm1_rtx)
+    ASM_GENERATE_INTERNAL_LABEL (landing_pad, "L",
+                                 CODE_LABEL_NUMBER (ci->landing_pad));
+
+  switch (ci->region_type)
+    {
+    case ECHT_ACTION_CHAIN:
+      dw2_asm_output_comment ("Region %d -- Action Chain", regno);
+      break;
+
+    case ECHT_CLEANUP:
+      dw2_asm_output_comment ("Region %d -- Cleanup", regno);
+      break;
+
+    case ECHT_DUMMY_ACTION_CHAIN:
+      dw2_asm_output_comment ("Region %d -- Dummy Action Chain", regno);
+      break;
+	
+    case ECHT_CONTINUE_UNWINDING:
+      dw2_asm_output_comment ("Region %d -- Continue Unwinding", regno);
+      break;
+    case ECHT_NOTHROW:
+      dw2_asm_output_comment ("Region %d -- NoThrow", regno);
+      break;
+    }
+
+  if (ci->region_type == ECHT_DUMMY_ACTION_CHAIN)
+    dw2_asm_output_data_uleb128 (0, "Zero length region");
+
+  else if (ci->region_type != ECHT_NOTHROW)
+    dw2_asm_output_compact_region_length (region_end,
+					  region_start, false, "Length");
+  else
+    dw2_asm_output_compact_region_length (region_end,
+					  region_start, true, "Length");
+
+  if (ci->landing_pad == constm1_rtx)
+    dw2_asm_output_data_sleb128 (-1, "landing pad");
+  else if (ci->landing_pad != NULL_RTX)
+    dw2_asm_output_compact_landing_pad (landing_pad,
+					region_end, "Landing Pad Offset");
+}
+
+/* Build a region table entry for a dummy nothrow.  */
+
+static void
+compact_insert_nothrow (int section, int prev_dwarf_region_no)
+{
+  compact_info_d *ci = ggc_cleared_alloc<compact_info_d> ();
+
+  ci->region_type = ECHT_NOTHROW;
+  ci->prev_dwarf_region_no = prev_dwarf_region_no;
+  ci->next_dwarf_region_no = prev_dwarf_region_no + 1;
+  ci->end_of_chain = true;
+
+  /* The beginning label of a dummy nothrow region is either
+     the beginning of the function or the end label of the
+     previous dwarf region.  */
+  if (vec_safe_length (compact_info) == 0)
+    {
+      ci->begin_lab_prefix = section == 0 ? func_begin_lab : switch_begin_lab;
+      ci->begin_lab_no = current_function_funcdef_no;
+    }
+  else
+    {
+      ci->begin_lab_prefix = region_end_lab;
+      ci->begin_lab_no = call_site_base + prev_dwarf_region_no;
+    }
+
+  /* The end label of a dummy nothrow region is the
+     beginning of the next dwarf region.  */
+  ci->end_lab_prefix = region_begin_lab;
+  ci->end_lab_no = call_site_base + prev_dwarf_region_no + 1;
+
+  vec_safe_push (compact_info, ci);
+}
+
+/* Build a region table entry for a dwarf-based continue-unwind region.  */
+static void
+compact_insert_continue (int dwarf_regno)
+{
+  compact_info_d *ci = ggc_cleared_alloc<compact_info_d> ();
+
+  ci->region_type = ECHT_CONTINUE_UNWINDING;
+  ci->dwarf_region_no = dwarf_regno;
+  ci->landing_pad = constm1_rtx;
+  ci->prev_dwarf_region_no = dwarf_regno - 1;
+  ci->next_dwarf_region_no = dwarf_regno + 1;
+  ci->end_of_chain = true;
+  ci->begin_lab_prefix = region_begin_lab;
+  ci->end_lab_prefix = region_end_lab;
+  ci->begin_lab_no = call_site_base + dwarf_regno;
+  ci->end_lab_no = call_site_base + dwarf_regno;
+  vec_safe_push (compact_info, ci);
+}
+
+/* Build a region table entry for a dwarf-based cleanup region.  */
+static void
+compact_insert_cleanup (int dwarf_regno, rtx lp)
+{
+  compact_info_d *ci = ggc_cleared_alloc<compact_info_d> ();
+
+  ci->region_type = ECHT_CLEANUP;
+  ci->dwarf_region_no = dwarf_regno;
+  ci->landing_pad = lp;
+  ci->end_of_chain = true;
+  ci->begin_lab_prefix = region_begin_lab;
+  ci->end_lab_prefix = region_end_lab;
+  ci->begin_lab_no = call_site_base + dwarf_regno;
+  ci->end_lab_no = call_site_base + dwarf_regno;
+
+  vec_safe_push (compact_info, ci);
+}
+
+/* Set up the chain values for the dummies.  */
+
+static void
+compact_link_to_dummy (int dummy_index)
+{
+  int i;
+  compact_info_d *ci;
+  for (i = 0; vec_safe_iterate (compact_info, i, &ci); ++i)
+    {
+      if (ci->use_dummy
+          && ci->dummy_index == dummy_index)
+	ci->chain_value = vec_safe_length (compact_info) - i;
+    }
+}
+
+/* Emit the dummy action chain region entries.  */
+
+static void
+compact_emit_dummies (void)
+{
+  int i;
+  dummy_info_ref di;
+
+  
+  for (i = 0; vec_safe_iterate (dummy_info, i, &di); ++i)
+    {
+      compact_info_d *ci = ggc_cleared_alloc<compact_info_d> ();
+      compact_link_to_dummy (i);
+      ci->region_type = ECHT_DUMMY_ACTION_CHAIN;
+      ci->action_value = di->action_value;
+      ci->chain_value = di->chain_value;
+      vec_safe_push (compact_info, ci);
+    }
+}
+
+/* Given an offset into the dwarf action table, return the region
+   number of a dummy action record that matches it.  */
+
+static bool
+compact_find_dummy (int looking_for, int *goto_dummy)
+{
+  int i;
+  dummy_info_ref di;
+
+  for (i = 0; vec_safe_iterate (dummy_info, i, &di); ++i)
+    {
+      if (looking_for == di->action_ndx)
+	{
+	  *goto_dummy = i;
+	  return true;
+	}
+    }
+  return false;
+}
+
+/* Traverse the base chain and a chain entry until the 
+   chains match or the end of one or both chains is reached.  */
+
+static bool
+compact_chains_match (chain_d *base_ce, chain_d *goto_ce)
+{
+
+  /* No match if the action indices are different.  */
+  if (base_ce->action_ndx != goto_ce->action_ndx)
+    return false;
+
+  /* Match if we reach the end of both chains at the same time.  */
+  if (base_ce->next == NULL
+      && goto_ce->next == NULL)
+    return true;
+
+  /* No match if we reach the end of one entry.  */
+  if (base_ce->next == NULL
+      || goto_ce->next == NULL)
+    return false;
+
+  return compact_chains_match (base_ce->next, goto_ce->next);
+
+}
+
+/* Given a dwarf region (base), search the chain entries to
+   determine if an existing entry matches the base chain.  */ 
+
+static bool
+compact_find_chain (int dwarf_regno, int *goto_regno)
+{
+  int i;
+  chain_ref ce;
+  chain_ref base_ce = (*chains)[dwarf_regno];
+
+  for (i = 0; vec_safe_iterate (chains, i, &ce); ++i)
+    {
+      /* Don't match self.  */
+      if (base_ce->dwarf_regno == ce->dwarf_regno)
+	continue;
+
+      /* Don't match if the next action index of the base is different than this
+	 entry's initial action index. */
+      if (base_ce->next->action_ndx != ce->action_ndx)
+	continue;
+
+      /* Match subsequent chain entries.  */
+      if (compact_chains_match (base_ce->next, ce))
+	{
+	  *goto_regno = i;
+	  return true;
+	}
+    }
+  return false;
+}
+
+/* Insert a dummy action chain in the regions list.  */
+
+static void
+compact_insert_dummy (int filter, int index, bool first, compact_info_d *ci)
+{
+  dummy_info_d *di = ggc_cleared_alloc<dummy_info_d> ();
+
+  /* If this is not the first dummy in this chain, then
+     the last dummy entry chains here.  */
+  if (!first)
+    {
+      int i;
+      dummy_info_ref di_prev;
+      
+      i = dummy_info->length () - 1;
+      di_prev = (*dummy_info)[i];
+      di_prev->chain_value = 1;
+    }
+
+  di->action_value = filter;
+  di->action_ndx = index;
+  
+  vec_safe_push (dummy_info, di);
+
+  /* If this is the first dummy in this chain, record the
+     chain-to-this-dummy info in the main region.  */
+  if (first)
+    {
+      ci->use_dummy = true;
+      ci->dummy_index = dummy_info->length( ) - 1;
+    }
+}
+
+/* Return the number of nothrow or continue-unwind regions between
+   two region region entries.  */
+static int
+compact_count_intervening_nocount (int lower, int higher)
+{
+  int i;
+  int ignore = 0;
+
+  if (vec_safe_is_empty (compact_info))
+    return ignore;
+
+  for (i = lower; i <= higher; i++)
+    {
+      compact_info_ref ci = (*compact_info)[i];
+  
+      if (ci->region_type == ECHT_NOTHROW
+	  || ci->region_type == ECHT_CONTINUE_UNWINDING)
+	ignore++;
+    }
+
+  return ignore;
+}
+
+/* Walk the DWARF2 exception-handling tables and emit a compact-encoding
+   of the information partitioned like so:
+
+   1.  A uleb128 offset to the end of the region data
+   2.  The region data
+   3.  Exception specification tables, if present
+   4.  Alignment padding
+   5.  Array of type data
+*/
+
+#define USE_ACTION_EXTENSION 2
+
+static void
+output_one_function_compact_eh_table (int section)
+{
+  int i;
+  unsigned int k;
+  uchar uc;
+  char cs_begin[32];
+  char cs_end[32];
+  char ehspec_begin[32];
+  char ehspec_end[32];
+  bool empty_exception_spec = false;
+  compact_info_d *ci, *ci2;
+
+  unsigned int action_value;
+  int action_extension = 0;
+  int no_ehspecs, regno;
+  int tt_format = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/1);
+  int tt_format_size = size_of_encoded_value (tt_format);
+  const unsigned char *ar_start =
+    vec_safe_address (crtl->eh.action_record_data);
+  
+  int nregions = vec_safe_length ((&x_rtl)->eh.call_site_record_v[section]);
+
+  targetm.asm_out.internal_label (asm_out_file, section ? "LLSDAC" : "LLSDA",
+                                  current_function_funcdef_no);
+
+  ASM_GENERATE_INTERNAL_LABEL (cs_begin, section ? "LLSDACSBC" : "LLSDACSB",
+                               current_function_funcdef_no);
+
+  ASM_GENERATE_INTERNAL_LABEL (cs_end, section ? "LLSDACSEC" : "LLSDACSE",
+                               current_function_funcdef_no);
+
+  no_ehspecs = cfun->eh->ehspec_data.other->length ();
+
+  if (no_ehspecs > 0)
+    dw2_asm_output_data (1, 0x42, "PR 2, EHspecs present");
+  else
+    dw2_asm_output_data (1, 0x2, "PR 2");
+  
+  if (nregions == 0)
+    {
+      dw2_asm_output_data_uleb128 (0, "Call-site length");
+      return;
+    }
+  else
+    dw2_asm_output_delta_uleb128 (cs_end, cs_begin, "Call-site length");
+
+  assemble_name (asm_out_file, cs_begin);
+  fputs (":\n", asm_out_file);
+
+  /* Build an action chain for each dwarf region.  */
+  for (i = 0; i < nregions; i++)
+    {
+      const unsigned char *p;
+      HOST_WIDE_INT ar_filter, ar_disp;
+      struct call_site_record_d *cs =
+	(*(&x_rtl)->eh.call_site_record_v[section])[i];
+      chain_d *ce = ggc_cleared_alloc<chain_d> ();
+      ce->dwarf_regno = i;
+
+      if (cs->action == 0)
+	vec_safe_push (chains, ce);
+      else
+	{
+	  p = ar_start + cs->action -1;
+	  p = read_sleb128 (p, &ar_filter);
+	  p = read_sleb128 (p, &ar_disp);
+	  ce->action_ndx = cs->action;
+	  vec_safe_push (chains, ce);
+	
+	  while (ar_disp != 0)
+	    {
+	      chain_d *next_ce = ggc_cleared_alloc<chain_d> ();
+	      ce->next = next_ce;
+	      next_ce->dwarf_regno = i;
+
+	      p = ar_start + (p - ar_start) + ar_disp - 1;
+	      p = read_sleb128 (p, &ar_filter);
+	      p = read_sleb128 (p, &ar_disp);
+
+	      next_ce->action_ndx = (p - 1) - ar_start;
+	      ce = next_ce;
+	    }
+	}
+    }
+
+  /* Walk the regions again, this time building compact region entries.  */
+  for (i = 0, regno = 0; i < nregions; ++i)
+    {
+      /* i counts the dwarf regions.  regno counts compact regions.  */
+      const unsigned char *p;
+      HOST_WIDE_INT ar_filter, ar_disp;
+
+      const unsigned char *ar_start =
+	vec_safe_address (crtl->eh.action_record_data);
+
+      struct call_site_record_d *cs =
+	(*(&x_rtl)->eh.call_site_record_v[section])[i];
+
+      /* Create a dummy nothrow region prior to each dwarf region.  The
+	 dummy nothrow region spans the addresses between
+         the dwarf regions.  */
+      compact_insert_nothrow (section, i - 1);
+      regno++;
+
+      if (cs->action == 0 && cs->landing_pad == 0)
+	{
+	  compact_insert_continue (i);
+	}
+      else if (cs->action == 0)
+	{
+	  compact_insert_cleanup (i, cs->landing_pad);
+	}
+      else
+	{
+	  compact_info_d *ci = ggc_cleared_alloc<compact_info_d> ();
+
+	  p = vec_safe_address (crtl->eh.action_record_data)
+			   + cs->action - 1;
+
+	  p = read_sleb128 (p, &ar_filter);
+	  p = read_sleb128 (p, &ar_disp);
+
+	  
+	  if (ar_filter == 0)
+	    ci->region_type = ECHT_CLEANUP;
+	  else
+	    ci->region_type = ECHT_ACTION_CHAIN;
+
+	  ci->dwarf_region_no = i;
+	  ci->first_action_ndx = cs->action;
+	  ci->action_value = ar_filter;
+	  ci->next_action_ndx = cs->action + 1 + ar_disp;
+	  ci->landing_pad = cs->landing_pad;
+	  ci->begin_lab_prefix = region_begin_lab;
+	  ci->end_lab_prefix = region_end_lab;
+	  ci->begin_lab_no = call_site_base + ci->dwarf_region_no;
+	  ci->end_lab_no = call_site_base + ci->dwarf_region_no;
+	  vec_safe_push (compact_info, ci);
+
+	  /* A non-zero action record displacement denotes a chain.  If 
+	     the chain is already present in the compact region records,
+	     re-use the existing chain.  If not, dummy action region
+	     entries are created.  Re-use existing dummy action region
+	     entries if possible.  */
+
+	  if (ar_disp != 0)
+	    {
+	      int goto_regno, goto_dummy;
+
+	      if (compact_find_chain (ci->dwarf_region_no, &goto_regno))
+		ci->chain_value = goto_regno;
+	      else if (compact_find_dummy (ci->next_action_ndx, &goto_dummy))
+		{
+		  ci->use_dummy = true;
+		  ci->dummy_index = goto_dummy;
+		}
+	      else
+		{
+		  bool first_dummy = true;
+
+		  while (ar_disp != 0)
+		    {
+		      int ar_index;
+		      p = ar_start + (p - ar_start) + ar_disp - 1;
+		      ar_index = p - ar_start + 1;
+		      p = read_sleb128 (p, &ar_filter);
+		      p = read_sleb128 (p, &ar_disp);
+		      compact_insert_dummy (ar_filter, ar_index, first_dummy, ci);
+		      first_dummy = false;
+		    }
+		}
+	    }
+
+	  if (ar_disp == 0)
+	    ci->end_of_chain = true;
+	}
+    }
+
+
+  for (i = 0; vec_safe_iterate (compact_info, i, &ci); ++i)
+    {
+      int j;
+      if (ci->end_of_chain == true
+	  || ci->region_type == ECHT_DUMMY_ACTION_CHAIN)
+	continue;
+
+      for (j = 0; vec_safe_iterate (compact_info, j, &ci2); ++j)
+	{
+	  if (ci2->first_action_ndx == ci->next_action_ndx)
+	    {
+	      ci->chain_value = j - i;
+	      break;
+	    }
+	}     
+    }
+
+  /* The dummy action record entries are emitted last.  */
+  compact_emit_dummies ();
+
+  /* no-throw and continue-unwind regions are excluded in the chain values.  */
+  for (i = 0; vec_safe_iterate (compact_info, i, &ci); ++i)
+    {
+      int goto_regno;
+      int nocount = 0;
+      compact_info_ref ci_next = NULL;
+
+      if (i < (int) vec_safe_length (compact_info) - 1)
+	ci_next = (*compact_info)[i + 1];
+
+      if (ci->chain_value == 1
+	  && ci_next != NULL
+	  && ci_next->region_type == ECHT_DUMMY_ACTION_CHAIN)
+	continue;
+
+      if (ci->chain_value == 0)
+	continue;
+
+      goto_regno = i + ci->chain_value;
+      if (goto_regno > i)
+	{
+	  nocount = compact_count_intervening_nocount (i, goto_regno);
+	  ci->chain_value = ci->chain_value - nocount;
+	}
+      else
+	{
+	  nocount = compact_count_intervening_nocount (goto_regno, i);
+	  ci->chain_value = ci->chain_value + nocount;
+	}
+    }
+
+  /* Emit the region entries.  */
+
+  for (i = 0; vec_safe_iterate (compact_info, i, &ci); ++i)
+    {
+      compact_emit_region_header (i, ci);
+
+      if (ci->region_type == ECHT_ACTION_CHAIN
+	  || ci->region_type == ECHT_DUMMY_ACTION_CHAIN
+	  || ci->region_type == ECHT_CLEANUP)
+	{
+	  if (!IN_RANGE (ci->action_value, -1, 1))
+	    {
+	      action_value = USE_ACTION_EXTENSION;
+	      action_extension = ci->action_value;
+	    }
+	  else
+	    {
+	      if (ci->action_value == -1)
+		action_value = 3;
+	      else
+		action_value = ci->action_value;
+	    }
+	  dw2_asm_output_compact_ac_pair_sleb128 (action_value,
+						  ci->chain_value,
+						  "Action/Chain Pair");
+
+	  if (action_value == USE_ACTION_EXTENSION)
+	    dw2_asm_output_data_sleb128 (action_extension, "Action extension");
+	}
+
+    }
+  assemble_name (asm_out_file, cs_end);
+  fputs (":\n", asm_out_file);
+
+  /* Emit the exception specification indices.  */
+  if (no_ehspecs == 1)
+    { 
+      uchar ehspec = (*cfun->eh->ehspec_data.other)[0];
+      if (ehspec == 0)
+	empty_exception_spec = true;
+    }
+
+  if (no_ehspecs != 0)
+    {
+      if (empty_exception_spec)
+	{
+	  dw2_asm_output_data (1, 0x0, "Length of EH Specs");
+    	}
+      else
+	{
+	  ASM_GENERATE_INTERNAL_LABEL (ehspec_begin, section ? "LLSDATTDC" : "LLSDATTD",
+				       current_function_funcdef_no);
+
+	  ASM_GENERATE_INTERNAL_LABEL (ehspec_end, section ? "LLSDATTC" : "LLSDATT",
+				       current_function_funcdef_no);
+
+	  dw2_asm_output_delta_uleb128 (ehspec_end, ehspec_begin, "Length of EH Specs");
+
+	  assemble_name (asm_out_file, ehspec_begin);
+	  fputs (":\n", asm_out_file);
+
+	  k = no_ehspecs;
+	  for (k = 0;
+	       vec_safe_iterate (cfun->eh->ehspec_data.other, k, &uc); ++k)
+	    dw2_asm_output_data (1, uc, k ? NULL : "Exception specification table");
+
+	  assemble_name (asm_out_file, ehspec_end);
+	  fputs (":\n", asm_out_file);
+        }
+    }
+
+  fputs (".align 2\n", asm_out_file);
+
+  /* Emit the type table entries.  */
+  for (k = 0; k < cfun->eh->ttype_data->length (); k++)
+    {
+      tree type = (*cfun->eh->ttype_data)[k];
+      output_ttype (type, tt_format, tt_format_size);
+    }
+
+  call_site_base += nregions;
+  vec_free (chains);
+  vec_free (dummy_info);
+  vec_free (compact_info);
+}
+
+static void
 output_one_function_exception_table (int section)
 {
   int tt_format, cs_format, lp_format, i;
@@ -3094,7 +3902,7 @@ output_one_function_exception_table (int section)
 }
 
 void
-output_function_exception_table (const char *fnname)
+output_function_exception_table (const char *fnname, bool after_switch_section)
 {
   rtx personality = get_personality_function (current_function_decl);
 
@@ -3102,6 +3910,9 @@ void
   if (! crtl->uses_eh_lsda)
     return;
 
+  if (after_switch_section && !using_compact_pr ())
+    return;
+
   if (personality)
     {
       assemble_external_libcall (personality);
@@ -3115,9 +3926,19 @@ void
   /* If the target wants a label to begin the table, emit it here.  */
   targetm.asm_out.emit_except_table_label (asm_out_file);
 
-  output_one_function_exception_table (0);
-  if (crtl->eh.call_site_record_v[1])
-    output_one_function_exception_table (1);
+  if (using_compact_pr ())
+    {
+      if (after_switch_section || crtl->eh.call_site_record_v[1] == NULL)
+	output_one_function_compact_eh_table (0);
+      else
+	output_one_function_compact_eh_table (1);
+    }
+  else
+    {
+      output_one_function_exception_table (0);
+      if (crtl->eh.call_site_record_v[1])
+	output_one_function_exception_table (1);
+    }
 
   switch_to_section (current_function_section ());
 }
Index: gcc/except.h
===================================================================
--- gcc/except.h	(revision 231238)
+++ gcc/except.h	(working copy)
@@ -62,6 +62,15 @@ enum eh_region_type
   ERT_MUST_NOT_THROW
 };
 
+/* Region header types in the compact EH scheme.  */
+enum eh_compact_header_type
+{
+  ECHT_ACTION_CHAIN,
+  ECHT_CLEANUP,
+  ECHT_CONTINUE_UNWINDING,
+  ECHT_DUMMY_ACTION_CHAIN,
+  ECHT_NOTHROW
+};
 
 /* A landing pad for a given exception region.  Any transfer of control
    from the EH runtime to the function happens at a landing pad.  */
@@ -224,12 +233,14 @@ extern void for_each_eh_label (void (*) (rtx));
 
 extern void init_eh_for_function (void);
 
+extern int compact_pr_id (rtx);
+
 extern void remove_eh_landing_pad (eh_landing_pad);
 extern void remove_eh_handler (eh_region);
 extern void remove_unreachable_eh_regions (sbitmap);
 
 extern bool current_function_has_exception_handlers (void);
-extern void output_function_exception_table (const char *);
+extern void output_function_exception_table (const char *, bool);
 
 extern rtx expand_builtin_eh_pointer (tree);
 extern rtx expand_builtin_eh_filter (tree);
@@ -284,6 +295,8 @@ extern eh_landing_pad get_eh_landing_pad_from_rtx
 
 extern void finish_eh_generation (void);
 
+extern void push_uleb128 (vec<uchar, va_gc> **, unsigned int);
+
 struct GTY(()) throw_stmt_node {
   gimple *stmt;
   int lp_nr;
Index: gcc/dwarf2cfi.c
===================================================================
--- gcc/dwarf2cfi.c	(revision 231238)
+++ gcc/dwarf2cfi.c	(working copy)
@@ -3409,10 +3409,12 @@ dwarf2out_do_cfi_asm (void)
   /* Make sure the personality encoding is one the assembler can support.
      In particular, aligned addresses can't be handled.  */
   enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/2,/*global=*/1);
-  if ((enc & 0x70) != 0 && (enc & 0x70) != DW_EH_PE_pcrel)
+  if ((enc & 0x70) != 0 && (enc & 0x70) != DW_EH_PE_pcrel
+      && (enc & GCC_DW_EH_PE_special) == 0)
     return false;
   enc = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0,/*global=*/0);
-  if ((enc & 0x70) != 0 && (enc & 0x70) != DW_EH_PE_pcrel)
+  if ((enc & 0x70) != 0 && (enc & 0x70) != DW_EH_PE_pcrel
+      && (enc & GCC_DW_EH_PE_special) == 0)
     return false;
 
   /* If we can't get the assembler to emit only .debug_frame, and we don't need
Index: gcc/dwarf2asm.c
===================================================================
--- gcc/dwarf2asm.c	(revision 231238)
+++ gcc/dwarf2asm.c	(working copy)
@@ -85,6 +85,22 @@ dw2_asm_output_data_raw (int size, unsigned HOST_W
     }
 }
 
+/* Output a comment.  */
+void
+dw2_asm_output_comment (const char *comment, ...)
+{
+  va_list ap;
+
+  va_start (ap, comment);
+
+  if (flag_debug_asm && comment)
+    {
+      fprintf (asm_out_file, "\t%s ", ASM_COMMENT_START);
+      vfprintf (asm_out_file, comment, ap);
+    }
+  fputc ('\n', asm_out_file);
+}
+
 /* Output an immediate constant in a given SIZE in bytes.  */
 
 void
@@ -416,7 +432,7 @@ eh_data_format_name (int format)
 #if HAVE_DESIGNATED_INITIALIZERS
   __extension__ static const char * const format_names[256] = {
 #else
-  switch (format) {
+  switch (format & ~GCC_DW_EH_PE_special) {
 #endif
 
   S(DW_EH_PE_absptr, "absolute")
@@ -553,6 +569,7 @@ eh_data_format_name (int format)
 #if HAVE_DESIGNATED_INITIALIZERS
   };
 
+  format &= ~GCC_DW_EH_PE_special;
   gcc_assert (format >= 0 && format < 0x100 && format_names[format]);
 
   return format_names[format];
@@ -759,6 +776,99 @@ dw2_asm_output_delta_uleb128 (const char *lab1 ATT
   va_end (ap);
 }
 
+/* Output a compact EH region length entry. */
+void
+dw2_asm_output_compact_region_length (const char *lab1 ATTRIBUTE_UNUSED,
+				      const char *lab2 ATTRIBUTE_UNUSED,
+				      bool setbit0,
+				      const char *comment, ...)
+{
+  va_list ap;
+
+  va_start (ap, comment);
+
+#ifdef HAVE_AS_LEB128
+  fputs ("\t.uleb128 (", asm_out_file);
+  assemble_name (asm_out_file, lab1);
+  fputs ("-", asm_out_file);
+  assemble_name (asm_out_file, lab2);
+  fputs (")", asm_out_file);
+  if (setbit0)
+    fputs ("|1", asm_out_file);
+#else
+  gcc_unreachable ();
+#endif
+
+  if (flag_debug_asm && comment)
+    {
+      fprintf (asm_out_file, "\t%s ", ASM_COMMENT_START);
+      vfprintf (asm_out_file, comment, ap);
+    }
+  fputc ('\n', asm_out_file);
+
+  va_end (ap);
+}
+
+/* Output a compact EH landing pad entry.  */
+
+void
+dw2_asm_output_compact_landing_pad (const char *lab1 ATTRIBUTE_UNUSED,
+				    const char *lab2 ATTRIBUTE_UNUSED,
+				    const char *comment, ...)
+{
+  va_list ap;
+
+  va_start (ap, comment);
+
+#ifdef HAVE_AS_LEB128
+  fputs ("\t.sleb128 (", asm_out_file);
+  assemble_name (asm_out_file, lab1);
+  fputs ("-(", asm_out_file);
+  assemble_name (asm_out_file, lab2);
+  fputs ("))", asm_out_file);
+#else
+  gcc_unreachable ();
+#endif
+
+  if (flag_debug_asm && comment)
+    {
+      fprintf (asm_out_file, "\t%s ", ASM_COMMENT_START);
+      vfprintf (asm_out_file, comment, ap);
+    }
+  fputc ('\n', asm_out_file);
+
+  va_end (ap);
+}
+
+/* Output a compact EH action/chain pair.  */
+
+void
+dw2_asm_output_compact_ac_pair_sleb128 (int action, int chain_value,
+					const char *comment, ...)
+{
+  va_list ap;
+
+  va_start (ap, comment);
+
+#ifdef HAVE_AS_LEB128
+  fputs ("\t.sleb128 ", asm_out_file);
+  fputc ('(', asm_out_file);
+  fprintf (asm_out_file, "%#x", chain_value);
+  fputs ("<<2)|", asm_out_file);
+  fprintf (asm_out_file, "%#x", action);
+#else
+  gcc_unreachable ();
+#endif
+
+  if (flag_debug_asm && comment)
+    {
+      fprintf (asm_out_file, "\t%s ", ASM_COMMENT_START);
+      vfprintf (asm_out_file, comment, ap);
+    }
+  fputc ('\n', asm_out_file);
+
+  va_end (ap);
+}
 #if 0
 
 void
Index: gcc/dwarf2asm.h
===================================================================
--- gcc/dwarf2asm.h	(revision 231238)
+++ gcc/dwarf2asm.h	(working copy)
@@ -24,6 +24,9 @@ extern void dw2_assemble_integer (int, rtx);
 
 extern void dw2_asm_output_data_raw (int, unsigned HOST_WIDE_INT);
 
+extern void dw2_asm_output_comment (const char *, ...)
+     ATTRIBUTE_NULL_PRINTF_1;
+
 extern void dw2_asm_output_data (int, unsigned HOST_WIDE_INT,
 				 const char *, ...)
      ATTRIBUTE_NULL_PRINTF_3;
@@ -70,6 +73,17 @@ extern void dw2_asm_output_delta_uleb128 (const ch
 					  const char *, ...)
      ATTRIBUTE_NULL_PRINTF_3;
 
+extern void dw2_asm_output_compact_region_length (const char *, const char *,
+					          bool, const char *, ...)
+     ATTRIBUTE_NULL_PRINTF_4;
+
+extern void dw2_asm_output_compact_landing_pad (const char *, const char *,
+						const char *, ...)
+     ATTRIBUTE_NULL_PRINTF_3;
+
+extern void dw2_asm_output_compact_ac_pair_sleb128 (int, int, const char *, ...)
+     ATTRIBUTE_NULL_PRINTF_3;
+
 extern int size_of_uleb128 (unsigned HOST_WIDE_INT);
 extern int size_of_sleb128 (HOST_WIDE_INT);
 extern int size_of_encoded_value (int);
@@ -89,4 +103,8 @@ extern void dw2_asm_output_delta_sleb128 (const ch
      ATTRIBUTE_NULL_PRINTF_3;
 #endif
 
+/* Provide a bit that can be used to mark an EH encoding as requiring
+   special handling.  */
+#define GCC_DW_EH_PE_special 0x100
+
 #endif /* GCC_DWARF2ASM_H */
Index: gcc/targhooks.c
===================================================================
--- gcc/targhooks.c	(revision 231238)
+++ gcc/targhooks.c	(working copy)
@@ -1961,4 +1961,10 @@ default_optab_supported_p (int, machine_mode, mach
   return true;
 }
 
+void
+default_asm_output_cfi_endproc (void)
+{
+  fprintf (asm_out_file, "\t.cfi_endproc\n");
+}
+
 #include "gt-targhooks.h"
Index: gcc/targhooks.h
===================================================================
--- gcc/targhooks.h	(revision 231238)
+++ gcc/targhooks.h	(working copy)
@@ -207,6 +207,7 @@ extern bool default_class_likely_spilled_p (reg_cl
 extern unsigned char default_class_max_nregs (reg_class_t, machine_mode);
 
 extern enum unwind_info_type default_debug_unwind_info (void);
+extern void default_asm_output_cfi_endproc (void);
 
 extern void default_canonicalize_comparison (int *, rtx *, rtx *, bool);
 
Index: gcc/config/mips/elf.h
===================================================================
--- gcc/config/mips/elf.h	(revision 231238)
+++ gcc/config/mips/elf.h	(working copy)
@@ -48,3 +48,5 @@ along with GCC; see the file COPYING3.  If not see
 #define ENDFILE_SPEC "crtend%O%s crtn%O%s"
 
 #define NO_IMPLICIT_EXTERN_C 1
+
+#define MD_HAVE_COMPACT_EH 1
Index: gcc/config/mips/sde.h
===================================================================
--- gcc/config/mips/sde.h	(revision 231238)
+++ gcc/config/mips/sde.h	(working copy)
@@ -18,6 +18,11 @@ You should have received a copy of the GNU General
 along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
 
+#ifdef HAVE_GAS_EH_FRAME_ENTRY
+#undef MIPS_COMPACT_EH_PCREL
+#define MIPS_COMPACT_EH_PCREL 1
+#endif
+
 #undef DRIVER_SELF_SPECS
 #define DRIVER_SELF_SPECS						\
   /* Set the ISA for the default multilib.  */				\
Index: gcc/config/mips/mips.opt
===================================================================
--- gcc/config/mips/mips.opt	(revision 231238)
+++ gcc/config/mips/mips.opt	(working copy)
@@ -99,6 +99,10 @@ Enum(mips_code_readable_setting) String(pcrel) Val
 EnumValue
 Enum(mips_code_readable_setting) String(no) Value(CODE_READABLE_NO)
 
+mcompact-eh
+Target Var(TARGET_COMPACT_EH) Init(1)
+Use compact exception unwinding tables.
+
 mdivide-breaks
 Target Report RejectNegative Mask(DIVIDE_BREAKS)
 Use branch-and-break sequences to check for integer divide by zero.
Index: gcc/config/mips/mips-protos.h
===================================================================
--- gcc/config/mips/mips-protos.h	(revision 231238)
+++ gcc/config/mips/mips-protos.h	(working copy)
@@ -255,6 +255,7 @@ extern void mips_pop_asm_switch (struct mips_asm_s
 extern void mips_output_external (FILE *, tree, const char *);
 extern void mips_output_ascii (FILE *, const char *, size_t);
 extern const char *mips_output_tls_reloc_directive (rtx *);
+extern void mips_fixup_cfi_sections (void);
 extern void mips_output_aligned_decl_common (FILE *, tree, const char *,
 					     unsigned HOST_WIDE_INT,
 					     unsigned int);
Index: gcc/config/mips/mips.c
===================================================================
--- gcc/config/mips/mips.c	(revision 231238)
+++ gcc/config/mips/mips.c	(working copy)
@@ -61,6 +61,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "context.h"
 #include "builtins.h"
 #include "rtl-iter.h"
+#include "except.h"
+#include "debug.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -531,6 +533,8 @@ static mips_one_only_stub *mips16_rdhwr_stub;
 static mips_one_only_stub *mips16_get_fcsr_stub;
 static mips_one_only_stub *mips16_set_fcsr_stub;
 
+static bool done_cfi_sections;
+
 /* Index R is the smallest register class that contains register R.  */
 const enum reg_class mips_regno_to_class[FIRST_PSEUDO_REGISTER] = {
   LEA_REGS,        LEA_REGS,        M16_STORE_REGS,  V1_REG,
@@ -6528,6 +6532,41 @@ mips_start_unique_function (const char *name)
   putc ('\n', asm_out_file);
 }
 
+/* The LTO frontend only enables exceptions when it sees a function that
+   uses it.  This changes the return value of dwarf2out_do_frame, so we
+   have to check before every function.  */
+
+void
+mips_fixup_cfi_sections (void)
+{
+#ifdef MD_HAVE_COMPACT_EH
+  if (done_cfi_sections)
+    return;
+
+  if (!TARGET_COMPACT_EH)
+    return;
+
+  /* Output a .cfi_sections directive.  */
+  if (dwarf2out_do_frame ())
+    {
+      if (flag_unwind_tables || flag_exceptions)
+	{
+	  if (write_symbols == DWARF2_DEBUG
+	      || write_symbols == VMS_AND_DWARF2_DEBUG
+	      || flag_asynchronous_unwind_tables)
+	    fprintf (asm_out_file,
+		     "\t.cfi_sections .debug_frame, .eh_frame_entry\n");
+	  else
+	    fprintf (asm_out_file, "\t.cfi_sections .eh_frame_entry\n");
+	}
+      else
+	fprintf (asm_out_file, "\t.cfi_sections .debug_frame\n");
+      done_cfi_sections = true;
+    }
+#endif
+}
+
+
 /* Start a definition of function NAME.  MIPS16_P indicates whether the
    function contains MIPS16 code.  */
 
@@ -7111,7 +7150,11 @@ mips16_build_call_stub (rtx retval, rtx *fn_ptr, r
 
 	  /* "Save" $sp in itself so we don't use the fake CFA.
 	     This is: DW_CFA_val_expression r29, { DW_OP_reg29 }.  */
-	  fprintf (asm_out_file, "\t.cfi_escape 0x16,29,1,0x6d\n");
+	  if (dwarf2out_do_frame ()
+	      && (flag_unwind_tables || flag_exceptions))
+	    fprintf (asm_out_file, "\t.cfi_fde_data 0x5b\n");
+	  else
+	    fprintf (asm_out_file, "\t.cfi_escape 0x16,29,1,0x6d\n");
 
 	  /* Save the return address in $18.  The stub's caller knows
 	     that $18 might be clobbered, even though $18 is usually
@@ -8723,6 +8766,19 @@ mips_function_rodata_section (tree decl)
   return data_section;
 }
 
+/* Implement TARGET_ASM_INIT_SECTIONS.  */
+
+static void
+mips_asm_init_sections (void)
+{
+  if (TARGET_COMPACT_EH)
+    {
+      /* Let the assembler decide where to put the LSDA.  */
+      exception_section = get_unnamed_section (0, output_section_asm_op,
+					       "\t.cfi_inline_lsda 2");
+    }
+}
+
 /* Implement TARGET_IN_SMALL_DATA_P.  */
 
 static bool
@@ -8733,6 +8789,13 @@ mips_in_small_data_p (const_tree decl)
   if (TREE_CODE (decl) == STRING_CST || TREE_CODE (decl) == FUNCTION_DECL)
     return false;
 
+  /* Place eh-related data in sdata so that we can use a gprel32 reloc
+     to access it.  */
+  if (TARGET_64BIT
+      && TREE_CODE (decl) == VAR_DECL && DECL_NAME (decl)
+      && strncmp (IDENTIFIER_POINTER (DECL_NAME (decl)), "DW.ref.", 7) == 0)
+    return true;
+
   /* We don't yet generate small-data references for -mabicalls
      or VxWorks RTP code.  See the related -G handling in
      mips_option_override.  */
@@ -9227,6 +9290,8 @@ mips_file_start (void)
 {
   default_file_start ();
 
+  done_cfi_sections = false;
+
   /* Generate a special section to describe the ABI switches used to
      produce the resultant binary.  */
 
@@ -12081,6 +12146,175 @@ mips_expand_epilogue (bool sibcall_p)
       emit_insn_before (gen_mips_ehb (), insn);
     }
 }
+
+/* Add an opcode to *FDE to describe the necessary pushes of r30/r31, if
+   any.  */
+static void
+add_fp_ra_push (vec<uchar, va_gc> **fde)
+{
+  bool fp_needed = BITSET_P (cfun->machine->frame.mask, 30);
+  if (!BITSET_P (cfun->machine->frame.mask, RETURN_ADDR_REGNUM)
+      && !fp_needed)
+    return;
+
+  vec_safe_push (*fde, (uchar) 0x59);
+  if (fp_needed)
+    vec_safe_push (*fde, (uchar) ((30 << 3) | 1));
+  else
+    vec_safe_push (*fde, uchar (31 << 3));
+}
+
+/* Add an opcode to *FDE to describe an adjustment of the CFA register by
+   AMOUNT, given an assumed alignment ALIGN.  */
+static void
+mips_fdedata_cfaadjust (vec <uchar, va_gc> **fde,
+			HOST_WIDE_INT amount, int align)
+{
+  if (amount == 0)
+    return;
+
+  gcc_assert (amount % align == 0);
+  if (amount >= align * 129)
+    {
+      vec_safe_push (*fde, (uchar) 0x58);
+      push_uleb128 (fde, amount / align - 129);
+    }
+  else if (amount >= align * 65)
+    {
+      vec_safe_push (*fde, (uchar) 0x3f);
+      vec_safe_push (*fde, (uchar) (amount / align - 64));
+    }
+  else
+    vec_safe_push (*fde, (uchar) (amount / align - 1));
+}
+
+/* Implements TARGET_ASM_OUTPUT_CFI_ENDPROC.  Emit the .cfi_endproc
+   directive, and when emitting unwind information, also emit the
+   .cfi_fde_data directive.  */
+static void
+mips_cfi_endproc (void)
+{
+  int align = TARGET_NEWABI ? 16 : 8;
+  vec<uchar, va_gc> *fde = NULL;
+  int i, len, n;
+  HOST_WIDE_INT total, push_base;
+  bool any_saved = false;
+
+  if (!TARGET_COMPACT_EH
+      || (!flag_unwind_tables && !flag_exceptions)
+      || flag_asynchronous_unwind_tables)
+    {
+      fprintf (asm_out_file, ".cfi_endproc\n");
+      return;
+    }
+
+  fprintf (asm_out_file, "\t.cfi_fde_data ");
+
+  total = cfun->machine->frame.total_size;
+  if (cfun->machine->frame.num_fp > 0)
+    push_base = cfun->machine->frame.fp_sp_offset + UNITS_PER_HWFPVALUE;
+  else if (cfun->machine->frame.num_gp > 0)
+    push_base = cfun->machine->frame.gp_sp_offset + UNITS_PER_WORD;
+  else
+    push_base = total;
+
+  if (frame_pointer_needed)
+    {
+      if (HARD_FRAME_POINTER_REGNUM == 30)
+	vec_safe_push (fde, (uchar) 0x5E);
+      else
+	{
+	  int t = HARD_FRAME_POINTER_REGNUM - 16;
+	  gcc_assert (t < 8);
+	  vec_safe_push (fde, (uchar) (0x50 + t));
+	}
+      total -= cfun->machine->frame.hard_frame_pointer_offset;
+      push_base -= cfun->machine->frame.hard_frame_pointer_offset;
+    }
+
+  if (HARD_FRAME_POINTER_REGNUM == 30 && frame_pointer_needed)
+    gcc_assert (BITSET_P (cfun->machine->frame.mask, 30));
+
+  mips_fdedata_cfaadjust (&fde, push_base, align);
+  total -= push_base;
+
+  n = 0;
+  for (i = 31; i > 19; i--)
+    {
+      bool isset = BITSET_P (cfun->machine->frame.fmask, i);
+      if (isset)
+	n++;
+      if ((i == 20 || !isset) && n > 0)
+	{
+	  int first = i + (isset ? 0 : 1);
+	  if (first == 20)
+	    {
+	      if (n > 8)
+		vec_safe_push (fde, (uchar) (0x68 + n - 9));
+	      else
+		vec_safe_push (fde, (uchar) (0x60 + n - 1));
+	    }
+	  else
+	    {
+	      if (n > 8)
+		{
+		  vec_safe_push (fde, (uchar) 0x5a);
+		  vec_safe_push (fde, (uchar) (((first + 8) << 3) | (n - 9)));
+		  n = 8;
+		}
+	      vec_safe_push (fde, (uchar) 0x5a);
+	      vec_safe_push (fde, (uchar) ((first << 3) | (n - 1)));
+	    }
+	  n = 0;
+	}
+    }
+  gcc_assert (n == 0);
+
+  for (i = 23; i > 0; i--)
+    {
+      bool isset = BITSET_P (cfun->machine->frame.mask, i);
+      if (isset)
+	n++;
+      if ((i == 16 || i == 1 || !isset) && n > 0)
+	{
+	  int first = i + (isset ? 0 : 1);
+	  if (first == 16 && !any_saved
+	      && BITSET_P (cfun->machine->frame.mask, RETURN_ADDR_REGNUM))
+	    {
+	      if (BITSET_P (cfun->machine->frame.mask, 30))
+		vec_safe_push (fde, (uchar) (0x48 + n - 1));
+	      else
+		vec_safe_push (fde, (uchar) (0x40 + n - 1));
+	    }
+	  else
+	    {
+	      if (!any_saved)
+		add_fp_ra_push (&fde);
+	      vec_safe_push (fde, (uchar) 0x59);
+	      vec_safe_push (fde, (uchar) ((first << 3) | (n - 1)));
+	    }
+	  any_saved = true;
+	  n = 0;
+	}
+    }
+  gcc_assert (n == 0);
+
+  if (!any_saved)
+    add_fp_ra_push (&fde);
+
+  /* Skip varargs save area and suchlike.  */
+  mips_fdedata_cfaadjust (&fde, total, align);
+
+  /* The assembler appends a "Finish" opcode for out-of-line entries;
+     the unwind code (uw_frame_state_compact) for inline entries.  */
+
+  len = vec_safe_length (fde);
+  for (i = 0; i < len; i++)
+    fprintf (asm_out_file, "0x%x%s", (*fde)[i],
+	     i + 1 == len ? "" : ",");
+  fputc ('\n', asm_out_file);
+  fprintf (asm_out_file, "\t.cfi_endproc\n");
+}
 \f
 /* Return nonzero if this function is known to have a null epilogue.
    This allows the optimizer to omit jumps to jumps if no stack
@@ -17731,6 +17965,10 @@ mips_option_override (void)
   SUBTARGET_OVERRIDE_OPTIONS;
 #endif
 
+#if !(defined(MD_HAVE_COMPACT_EH) && defined(HAVE_GAS_EH_FRAME_ENTRY))
+  TARGET_COMPACT_EH = 0;
+#endif
+
   /* MIPS16 and microMIPS cannot coexist.  */
   if (TARGET_MICROMIPS && TARGET_MIPS16)
     error ("unsupported combination: %s", "-mips16 -mmicromips");
@@ -17981,6 +18219,10 @@ mips_option_override (void)
 	     mips_arch_info->name, "-mno-explicit-relocs");
     }
 
+  /* Compact EH is only supported for the o32/n32/n64 ABIs.  */
+  if (TARGET_COMPACT_EH && !ABI_HAS_COMPACT_EH_SUPPORT)
+    TARGET_COMPACT_EH = 0;
+
   /* The effect of -mabicalls isn't defined for the EABI.  */
   if (mips_abi == ABI_EABI && TARGET_ABICALLS)
     {
@@ -19929,6 +20171,8 @@ mips_ira_change_pseudo_allocno_class (int regno, r
 #define TARGET_ASM_SELECT_RTX_SECTION mips_select_rtx_section
 #undef TARGET_ASM_FUNCTION_RODATA_SECTION
 #define TARGET_ASM_FUNCTION_RODATA_SECTION mips_function_rodata_section
+#undef TARGET_ASM_INIT_SECTIONS
+#define TARGET_ASM_INIT_SECTIONS mips_asm_init_sections
 
 #undef TARGET_SCHED_INIT
 #define TARGET_SCHED_INIT mips_sched_init
@@ -20139,6 +20383,9 @@ mips_ira_change_pseudo_allocno_class (int regno, r
 #undef TARGET_ASM_OUTPUT_SOURCE_FILENAME
 #define TARGET_ASM_OUTPUT_SOURCE_FILENAME mips_output_filename
 
+#undef TARGET_OUTPUT_CFI_ENDPROC
+#define TARGET_OUTPUT_CFI_ENDPROC mips_cfi_endproc
+
 #undef TARGET_SHIFT_TRUNCATION_MASK
 #define TARGET_SHIFT_TRUNCATION_MASK mips_shift_truncation_mask
 
Index: gcc/config/mips/mips.h
===================================================================
--- gcc/config/mips/mips.h	(revision 231238)
+++ gcc/config/mips/mips.h	(working copy)
@@ -619,6 +619,9 @@ struct mips_cpu_info {
 									\
       if (TARGET_CACHE_BUILTIN)						\
 	builtin_define ("__GCC_HAVE_BUILTIN_MIPS_CACHE");		\
+									\
+      if (TARGET_COMPACT_EH)						\
+	builtin_define ("__GNU_COMPACT_EH__");				\
     }									\
   while (0)
 
@@ -888,6 +891,9 @@ struct mips_cpu_info {
 				 && Pmode == DImode	\
 				 && !TARGET_SYM32)
 
+/* True if Compact EH supports the ABI.  */
+#define ABI_HAS_COMPACT_EH_SUPPORT (mips_abi == ABI_32 || TARGET_NEWABI)
+
 /* ISA has instructions for managing 64-bit fp and gp regs (e.g. mips3).  */
 #define ISA_HAS_64BIT_REGS	(ISA_MIPS3				\
 				 || ISA_MIPS4				\
@@ -2821,11 +2827,12 @@ while (0)
 /* This is how to declare a function name.  The actual work of
    emitting the label is moved to function_prologue, so that we can
    get the line number correctly emitted before the .ent directive,
-   and after any .file directives.  Define as empty so that the function
-   is not declared before the .ent directive elsewhere.  */
+   and after any .file directives.  Define as mostly empty so that the
+   function is not declared before the .ent directive elsewhere.  */
 
 #undef ASM_DECLARE_FUNCTION_NAME
-#define ASM_DECLARE_FUNCTION_NAME(STREAM,NAME,DECL)
+#define ASM_DECLARE_FUNCTION_NAME(STREAM,NAME,DECL) \
+  mips_fixup_cfi_sections ();
 
 /* This is how to store into the string LABEL
    the symbol_ref name of an internal numbered label where
@@ -3312,10 +3319,42 @@ struct GTY(())  machine_function {
    versions of the linker know how to do this for indirect pointers,
    and for personality data.  We must fall back on using writable
    .eh_frame sections for shared libraries if the linker does not
-   support this feature.  */
+   support this feature.
+   
+   For compact EH frames we have a special relocation we can use.  */
+
+#define MIPS_COMPACT_EH_PCREL 1
+
+#define MIPS_COMPACT_EH_ENCODING (DW_EH_PE_pcrel | DW_EH_PE_sdata4)
+
 #define ASM_PREFERRED_EH_DATA_FORMAT(CODE,GLOBAL) \
-  (((GLOBAL) ? DW_EH_PE_indirect : 0) | DW_EH_PE_absptr)
+  (TARGET_COMPACT_EH \
+   ? MIPS_COMPACT_EH_ENCODING \
+   : (((GLOBAL) ? DW_EH_PE_indirect : 0) | DW_EH_PE_absptr))
 
+/* Handle special EH pointer encodings.  */
+
+#define ASM_MAYBE_OUTPUT_ENCODED_ADDR_RTX(FILE, ENCODING, SIZE, ADDR, DONE) \
+  do {									\
+    if ((SIZE) == 4 && (ENCODING) == MIPS_EH_ENCODING)			\
+      {									\
+        fputs ("\t.ehword\t", FILE);					\
+        assemble_name (FILE, XSTR (ADDR, 0));				\
+        goto DONE;							\
+      }									\
+  } while (0)
+
+#define MIPS_EH_ENCODING \
+  (DW_EH_PE_datarel | DW_EH_PE_sdata4 | DW_EH_PE_indirect)
+
+#define md_unwind_compact_opcode_finish 0x5c
+
+#define CRT_GET_RFIB_DATA(BASE)			\
+  {						\
+    extern char _gp[];				\
+    BASE = &_gp[0];				\
+  }
+
 /* For switching between MIPS16 and non-MIPS16 modes.  */
 #define SWITCHABLE_TARGET 1
 
Index: gcc/config/mips/linux.h
===================================================================
--- gcc/config/mips/linux.h	(revision 231238)
+++ gcc/config/mips/linux.h	(working copy)
@@ -47,3 +47,8 @@ along with GCC; see the file COPYING3.  If not see
 #define GNU_USER_DYNAMIC_LINKERN32 \
   CHOOSE_DYNAMIC_LINKER (GLIBC_DYNAMIC_LINKERN32, UCLIBC_DYNAMIC_LINKERN32, \
                          BIONIC_DYNAMIC_LINKERN32, MUSL_DYNAMIC_LINKERN32)
+
+#define MD_HAVE_COMPACT_EH 1
+
+#undef ABI_HAS_COMPACT_EH_SUPPORT
+#define ABI_HAS_COMPACT_EH_SUPPORT (mips_abi == ABI_32)
Index: gcc/target.def
===================================================================
--- gcc/target.def	(revision 231238)
+++ gcc/target.def	(working copy)
@@ -918,6 +918,12 @@ DEFHOOK
  tree, (const char *name),
  default_mangle_assembler_name)
 
+DEFHOOK
+(output_cfi_endproc,
+ "Emit the @code{.cfi_endproc} directive at the end of a function, and\
+ optionally additional exception handling information for it.",
+ void, (void), default_asm_output_cfi_endproc)
+
 HOOK_VECTOR_END (asm_out)
 
 /* Functions relating to instruction scheduling.  All of these
Index: gcc/defaults.h
===================================================================
--- gcc/defaults.h	(revision 231238)
+++ gcc/defaults.h	(working copy)
@@ -392,6 +392,10 @@ see the files COPYING3 and COPYING.RUNTIME respect
 #define MASK_RETURN_ADDR NULL_RTX
 #endif
 
+#ifndef TARGET_COMPACT_EH
+#define TARGET_COMPACT_EH 0
+#endif
+
 /* If we have named section and we support weak symbols, then use the
    .jcr section for recording java classes which need to be registered
    at program start-up time.  */
Index: gcc/configure
===================================================================
--- gcc/configure	(revision 231238)
+++ gcc/configure	(working copy)
@@ -27881,6 +27881,37 @@ _ACEOF
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_ld_compress_debug" >&5
 $as_echo "$gcc_cv_ld_compress_debug" >&6; }
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for CFI .eh_frame_entry" >&5
+$as_echo_n "checking assembler for CFI .eh_frame_entry... " >&6; }
+if test "${gcc_cv_as_eh_frame_entry+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  gcc_cv_as_eh_frame_entry=no
+  if test x$gcc_cv_as != x; then
+    $as_echo '	.cfi_sections .eh_frame_entry ' > conftest.s
+    if { ac_try='$gcc_cv_as $gcc_cv_as_flags  -o conftest.o conftest.s >&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+    then
+	gcc_cv_as_eh_frame_entry=yes
+    else
+      echo "configure: failed program was" >&5
+      cat conftest.s >&5
+    fi
+    rm -f conftest.o conftest.s
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_as_eh_frame_entry" >&5
+$as_echo "$gcc_cv_as_eh_frame_entry" >&6; }
+if test $gcc_cv_as_eh_frame_entry = yes; then
+
+$as_echo "#define HAVE_GAS_EH_FRAME_ENTRY 1" >>confdefs.h
+
+fi
+
 # --------
 # UNSORTED
 # --------
Index: gcc/doc/tm.texi
===================================================================
--- gcc/doc/tm.texi	(revision 231238)
+++ gcc/doc/tm.texi	(working copy)
@@ -8261,6 +8261,10 @@ systems.  This macro is used in @code{assemble_nam
 Given a symbol @var{name}, perform same mangling as @code{varasm.c}'s @code{assemble_name}, but in memory rather than to a file stream, returning result as an @code{IDENTIFIER_NODE}.  Required for correct LTO symtabs.  The default implementation calls the @code{TARGET_STRIP_NAME_ENCODING} hook and then prepends the @code{USER_LABEL_PREFIX}, if any.
 @end deftypefn
 
+@deftypefn {Target Hook} void TARGET_OUTPUT_CFI_ENDPROC (void)
+Emit the @code{.cfi_endproc} directive at the end of a function, and optionally additional exception handling information for it.
+@end deftypefn
+
 @defmac ASM_OUTPUT_SYMBOL_REF (@var{stream}, @var{sym})
 A C statement (sans semicolon) to output a reference to
 @code{SYMBOL_REF} @var{sym}.  If not defined, @code{assemble_name}
Index: gcc/doc/invoke.texi
===================================================================
--- gcc/doc/invoke.texi	(revision 231238)
+++ gcc/doc/invoke.texi	(working copy)
@@ -825,7 +825,7 @@ Objective-C and Objective-C++ Dialects}.
 -mfp-exceptions -mno-fp-exceptions @gol
 -mvr4130-align -mno-vr4130-align -msynci -mno-synci @gol
 -mrelax-pic-calls -mno-relax-pic-calls -mmcount-ra-address @gol
--mframe-header-opt -mno-frame-header-opt}
+-mframe-header-opt -mno-frame-header-opt -mcompact-eh -mno-compact-eh}
 
 @emph{MMIX Options}
 @gccoptlist{-mlibfuncs  -mno-libfuncs  -mepsilon  -mno-epsilon  -mabi=gnu @gol
@@ -18274,6 +18274,13 @@ it is unused.
 
 This optimization is off by default at all optimization levels.
 
+@item -mno-compact-eh
+@itemx -mcompact-eh
+@opindex mno-compact-eh
+@opindex mcompact-eh
+Disable generation of compact EH frame tables.
+@option{-mcompact-eh} is the default where supported.
+
 @end table
 
 @node MMIX Options
Index: gcc/doc/tm.texi.in
===================================================================
--- gcc/doc/tm.texi.in	(revision 231238)
+++ gcc/doc/tm.texi.in	(working copy)
@@ -5794,6 +5794,8 @@ systems.  This macro is used in @code{assemble_nam
 
 @hook TARGET_MANGLE_ASSEMBLER_NAME
 
+@hook TARGET_OUTPUT_CFI_ENDPROC
+
 @defmac ASM_OUTPUT_SYMBOL_REF (@var{stream}, @var{sym})
 A C statement (sans semicolon) to output a reference to
 @code{SYMBOL_REF} @var{sym}.  If not defined, @code{assemble_name}
Index: libgcc/unwind-dw2.c
===================================================================
--- libgcc/unwind-dw2.c	(revision 231238)
+++ libgcc/unwind-dw2.c	(working copy)
@@ -396,6 +396,12 @@ _Unwind_GetTextRelBase (struct _Unwind_Context *co
 {
   return (_Unwind_Ptr) context->bases.tbase;
 }
+
+unsigned char
+_Unwind_GetEhEncoding (struct _Unwind_Context *context)
+{
+  return context->bases.eh_encoding;
+}
 #endif
 
 #include "md-unwind-support.h"
@@ -1219,6 +1225,75 @@ execute_cfa_program (const unsigned char *insn_ptr
     }
 }
 \f
+#ifdef MD_HAVE_COMPACT_EH
+static _Unwind_Reason_Code
+__gnu_compact_pr1 (int version ATTRIBUTE_UNUSED,
+		  _Unwind_Action actions ATTRIBUTE_UNUSED,
+		  _Unwind_Exception_Class exception_class ATTRIBUTE_UNUSED,
+		  struct _Unwind_Exception *ue_header ATTRIBUTE_UNUSED,
+		  struct _Unwind_Context *context ATTRIBUTE_UNUSED)
+{
+  return _URC_CONTINUE_UNWIND;
+}
+
+/* The C++ EH routines need to live in the C++ runtime.  These should
+   be pulled in via the unwinding tables when needed.  */
+extern _Unwind_Reason_Code __gnu_compact_pr2 (int, _Unwind_Action,
+  _Unwind_Exception_Class, struct _Unwind_Exception *,
+  struct _Unwind_Context *) TARGET_ATTRIBUTE_WEAK;
+
+/* Invoke the Compact EH unwinder.  */
+
+static _Unwind_Reason_Code
+uw_frame_state_compact (struct _Unwind_Context *context,
+			_Unwind_FrameState *fs,
+			enum compact_entry_type entry_type,
+		       	struct compact_eh_bases *bases)
+{
+  const unsigned char *p;
+  unsigned int pr_index;
+  _Unwind_Ptr personality;
+  unsigned char buf[4];
+  _Unwind_Reason_Code rc;
+
+  p = bases->entry;
+  pr_index = *(p++);
+  switch (pr_index) {
+  case 0:
+      p = read_encoded_value (context, bases->eh_encoding, p, &personality);
+      fs->personality = (_Unwind_Personality_Fn) personality;
+      break;
+  case 1:
+      fs->personality = __gnu_compact_pr1;
+      break;
+  case 2:
+      fs->personality = __gnu_compact_pr2;
+      break;
+  default:
+      fs->personality = NULL;
+  }
+
+  if (!fs->personality)
+    return _URC_FATAL_PHASE1_ERROR;
+
+  if (entry_type == CET_inline)
+    {
+      memcpy(buf, p, 3);
+      buf[3] = md_unwind_compact_opcode_finish;
+      p = buf;
+    }
+
+  rc = md_unwind_compact (context, fs, &p);
+  if (rc != _URC_NO_REASON)
+      return rc;
+
+  if (entry_type == CET_outline)
+    context->lsda = (void *)(_Unwind_Internal_Ptr) p;
+
+  return _URC_NO_REASON;
+}
+#endif
+
 /* Given the _Unwind_Context CONTEXT for a stack frame, look up the FDE for
    its caller and decode it into FS.  This function also sets the
    args_size and lsda members of CONTEXT, as they are really information
@@ -1238,8 +1313,27 @@ uw_frame_state_for (struct _Unwind_Context *contex
   if (context->ra == 0)
     return _URC_END_OF_STACK;
 
+#ifdef MD_HAVE_COMPACT_EH
+    {
+      struct compact_eh_bases bases;
+      enum compact_entry_type type;
+      type = _Unwind_Find_Index (context->ra + _Unwind_IsSignalFrame (context)
+				 - 1, &bases);
+      context->bases.tbase = bases.tbase;
+      context->bases.dbase = bases.dbase;
+      context->bases.func = bases.func;
+      context->bases.eh_encoding = bases.eh_encoding;
+      if (type == CET_inline || type == CET_outline)
+	return uw_frame_state_compact (context, fs, type, &bases);
+      if (type == CET_FDE)
+	fde = bases.entry;
+      else
+	fde = NULL;
+    }
+#else
   fde = _Unwind_Find_FDE (context->ra + _Unwind_IsSignalFrame (context) - 1,
 			  &context->bases);
+#endif
   if (fde == NULL)
     {
 #ifdef MD_FALLBACK_FRAME_STATE_FOR
@@ -1559,9 +1653,6 @@ uw_init_context_1 (struct _Unwind_Context *context
   if (!ASSUME_EXTENDED_UNWIND_CONTEXT)
     context->flags = EXTENDED_CONTEXT_BIT;
 
-  code = uw_frame_state_for (context, &fs);
-  gcc_assert (code == _URC_NO_REASON);
-
 #if __GTHREADS
   {
     static __gthread_once_t once_regsizes = __GTHREAD_ONCE_INIT;
@@ -1574,6 +1665,9 @@ uw_init_context_1 (struct _Unwind_Context *context
     init_dwarf_reg_size_table ();
 #endif
 
+  code = uw_frame_state_for (context, &fs);
+  gcc_assert (code == _URC_NO_REASON);
+
   /* Force the frame state to use the known cfa value.  */
   _Unwind_SetSpColumn (context, outer_cfa, &sp_slot);
   fs.regs.cfa_how = CFA_REG_OFFSET;
@@ -1710,6 +1804,7 @@ alias (_Unwind_Resume);
 alias (_Unwind_Resume_or_Rethrow);
 alias (_Unwind_SetGR);
 alias (_Unwind_SetIP);
+alias (_Unwind_GetEhEncoding);
 #endif
 
 #endif /* !USING_SJLJ_EXCEPTIONS */
Index: libgcc/libgcc-std.ver.in
===================================================================
--- libgcc/libgcc-std.ver.in	(revision 231238)
+++ libgcc/libgcc-std.ver.in	(working copy)
@@ -1938,3 +1938,9 @@ GCC_4.7.0 {
 %inherit GCC_4.8.0 GCC_4.7.0
 GCC_4.8.0 {
 }
+
+%inherit GCC_6.0 GCC_4.7.0
+GCC_6.0 {
+  _Unwind_GetEhEncoding
+  __register_frame_info_header_bases
+}
Index: libgcc/config.host
===================================================================
--- libgcc/config.host	(revision 231238)
+++ libgcc/config.host	(working copy)
@@ -863,6 +863,7 @@ mips*-*-linux*)				# Linux MIPS, either endian.
 	;;
 mips*-sde-elf*)
 	tmake_file="$tmake_file mips/t-crtstuff mips/t-mips16"
+	md_unwind_header=mips/mips-unwind.h
 	case "${with_newlib}" in
 	  yes)
 	    # newlib / libgloss.
Index: libgcc/Makefile.in
===================================================================
--- libgcc/Makefile.in	(revision 231238)
+++ libgcc/Makefile.in	(working copy)
@@ -330,7 +330,7 @@ TPBIT_FUNCS = _pack_tf _unpack_tf _addsub_tf _mul_
 
 # Additional sources to handle exceptions; overridden by targets as needed.
 LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde.c \
-  $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c
+  $(srcdir)/unwind-compact.c $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c
 LIB2ADDEHSTATIC = $(LIB2ADDEH)
 LIB2ADDEHSHARED = $(LIB2ADDEH)
 
Index: libgcc/unwind-dw2-fde-dip.c
===================================================================
--- libgcc/unwind-dw2-fde-dip.c	(revision 231238)
+++ libgcc/unwind-dw2-fde-dip.c	(working copy)
@@ -45,6 +45,9 @@
 #include "unwind-dw2-fde.h"
 #include "unwind-compat.h"
 #include "gthr.h"
+#ifdef MD_HAVE_COMPACT_EH
+#include "unwind-compact.h"
+#endif
 
 #if !defined(inhibit_libc) && defined(HAVE_LD_EH_FRAME_HDR) \
     && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
@@ -90,11 +93,11 @@
 # define __RELOC_POINTER(ptr, base) ((ptr) + (base))
 #endif
 
-static const fde * _Unwind_Find_registered_FDE (void *pc, struct dwarf_eh_bases *bases);
+static enum compact_entry_type
+_Unwind_Find_registered_Index (void *pc, struct compact_eh_bases *bases);
 
-#define _Unwind_Find_FDE _Unwind_Find_registered_FDE
+#define _Unwind_Find_registered_Index _Unwind_Find_registered_Index
 #include "unwind-dw2-fde.c"
-#undef _Unwind_Find_FDE
 
 #ifndef PT_GNU_EH_FRAME
 #define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550)
@@ -103,10 +106,8 @@
 struct unw_eh_callback_data
 {
   _Unwind_Ptr pc;
-  void *tbase;
-  void *dbase;
-  void *func;
-  const fde *ret;
+  struct compact_eh_bases *bases;
+  enum compact_entry_type type;
   int check_cache;
 };
 
@@ -149,9 +150,9 @@ base_from_cb_data (unsigned char encoding, struct
       return 0;
 
     case DW_EH_PE_textrel:
-      return (_Unwind_Ptr) data->tbase;
+      return (_Unwind_Ptr) data->bases->tbase;
     case DW_EH_PE_datarel:
-      return (_Unwind_Ptr) data->dbase;
+      return (_Unwind_Ptr) data->bases->dbase;
     default:
       gcc_unreachable ();
     }
@@ -173,6 +174,7 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *
   _Unwind_Ptr eh_frame;
   struct object ob;
   _Unwind_Ptr pc_low = 0, pc_high = 0;
+  const fde *f;
 
   struct ext_dl_phdr_info
     {
@@ -321,12 +323,11 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *
   /* Read .eh_frame_hdr header.  */
   hdr = (const struct unw_eh_frame_hdr *)
     __RELOC_POINTER (p_eh_frame_hdr->p_vaddr, load_base);
-  if (hdr->version != 1)
-    return 1;
 
 #ifdef CRT_GET_RFIB_DATA
-# ifdef __i386__
-  data->dbase = NULL;
+# if defined(__i386__) || defined(__mips__)
+  data->bases->dbase = NULL;
+
   if (p_dynamic)
     {
       /* For dynamically linked executables and shared libraries,
@@ -336,8 +337,11 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *
       for (; dyn->d_tag != DT_NULL ; dyn++)
 	if (dyn->d_tag == DT_PLTGOT)
 	  {
-	    data->dbase = (void *) dyn->d_un.d_ptr;
-#if defined __linux__
+	    data->bases->dbase = (void *) dyn->d_un.d_ptr;
+#if defined(__mips__)
+	    /* MIPS targets need to relocated the value and the offset.  */
+	    data->bases->dbase += load_base + 0x7ff0;
+#elif defined __linux__
 	    /* On IA-32 Linux, _DYNAMIC is writable and GLIBC has
 	       relocated it.  */
 #elif defined __sun__ && defined __svr4__
@@ -354,6 +358,20 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *
 # endif
 #endif
 
+#ifdef MD_HAVE_COMPACT_EH
+  if (hdr->version == 2)
+    {
+      data->type =
+	_Unwind_Search_Compact_Eh_Hdr ((void *)data->pc,
+				       (const unsigned char *) hdr,
+				       data->bases);
+      return 1;
+    }
+#endif
+
+  if (hdr->version != 1)
+    return 1;
+
   p = read_encoded_value_with_base (hdr->eh_frame_ptr_enc,
 				    base_from_cb_data (hdr->eh_frame_ptr_enc,
 						       data),
@@ -384,7 +402,6 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *
 	  const struct fde_table *table = (const struct fde_table *) p;
 	  size_t lo, hi, mid;
 	  _Unwind_Ptr data_base = (_Unwind_Ptr) hdr;
-	  fde *f;
 	  unsigned int f_enc, f_enc_size;
 	  _Unwind_Ptr range;
 
@@ -416,8 +433,11 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *
 	  read_encoded_value_with_base (f_enc & 0x0f, 0,
 					&f->pc_begin[f_enc_size], &range);
 	  if (data->pc < table[mid].initial_loc + data_base + range)
-	    data->ret = f;
-	  data->func = (void *) (table[mid].initial_loc + data_base);
+	    {
+	      data->bases->entry = f;
+	      data->type = CET_FDE;
+	    }
+	  data->bases->func = (void *) (table[mid].initial_loc + data_base);
 	  return 1;
 	}
     }
@@ -426,58 +446,52 @@ _Unwind_IteratePhdrCallback (struct dl_phdr_info *
      As soon as GLIBC will provide API so to notify that a library has been
      removed, we could cache this (and thus use search_object).  */
   ob.pc_begin = NULL;
-  ob.tbase = data->tbase;
-  ob.dbase = data->dbase;
+  ob.tbase = data->bases->tbase;
+  ob.dbase = data->bases->dbase;
   ob.u.single = (fde *) eh_frame;
   ob.s.i = 0;
   ob.s.b.mixed_encoding = 1;  /* Need to assume worst case.  */
-  data->ret = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc);
-  if (data->ret != NULL)
+  f = linear_search_fdes (&ob, (fde *) eh_frame, (void *) data->pc);
+  data->bases->entry = f;
+  if (f != NULL)
     {
       _Unwind_Ptr func;
-      unsigned int encoding = get_fde_encoding (data->ret);
+      unsigned int encoding = get_fde_encoding (f);
 
       read_encoded_value_with_base (encoding,
 				    base_from_cb_data (encoding, data),
-				    data->ret->pc_begin, &func);
-      data->func = (void *) func;
+				    f->pc_begin, &func);
+      data->bases->func = (void *) func;
+      data->type = CET_FDE;
     }
   return 1;
 }
 
-const fde *
-_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
+enum compact_entry_type
+_Unwind_Find_Index (void *pc, struct compact_eh_bases *bases)
 {
   struct unw_eh_callback_data data;
-  const fde *ret;
 
-  ret = _Unwind_Find_registered_FDE (pc, bases);
-  if (ret != NULL)
-    return ret;
+  data.type = _Unwind_Find_registered_Index (pc, bases);
+  if (data.type != CET_not_found)
+    return data.type;
 
   data.pc = (_Unwind_Ptr) pc;
-  data.tbase = NULL;
-  data.dbase = NULL;
-  data.func = NULL;
-  data.ret = NULL;
+  data.bases = bases;
+  data.type = CET_not_found;
   data.check_cache = 1;
 
   if (dl_iterate_phdr (_Unwind_IteratePhdrCallback, &data) < 0)
-    return NULL;
+    return CET_not_found;
 
-  if (data.ret)
-    {
-      bases->tbase = data.tbase;
-      bases->dbase = data.dbase;
-      bases->func = data.func;
-    }
-  return data.ret;
+  return data.type;
 }
 
-#else
-/* Prevent multiple include of header files.  */
-#define _Unwind_Find_FDE _Unwind_Find_FDE
+#else /* !USE_PT_GNU_EH_FRAME */
+
+#define _Unwind_Find_registered_Index _Unwind_Find_Index
 #include "unwind-dw2-fde.c"
+
 #endif
 
 #if defined (USE_GAS_SYMVER) && defined (SHARED) && defined (USE_LIBUNWIND_EXCEPTIONS)
Index: libgcc/unwind-pe.h
===================================================================
--- libgcc/unwind-pe.h	(revision 231238)
+++ libgcc/unwind-pe.h	(working copy)
@@ -44,6 +44,8 @@
 #define DW_EH_PE_udata2         0x02
 #define DW_EH_PE_udata4         0x03
 #define DW_EH_PE_udata8         0x04
+#define DW_EH_PE_udata1         0x05
+#define DW_EH_PE_sdata1         0x06
 #define DW_EH_PE_sleb128        0x09
 #define DW_EH_PE_sdata2         0x0A
 #define DW_EH_PE_sdata4         0x0B
@@ -78,6 +80,8 @@ size_of_encoded_value (unsigned char encoding)
     {
     case DW_EH_PE_absptr:
       return sizeof (void *);
+    case DW_EH_PE_udata1:
+      return 1;
     case DW_EH_PE_udata2:
       return 2;
     case DW_EH_PE_udata4:
@@ -227,6 +231,10 @@ read_encoded_value_with_base (unsigned char encodi
 	  }
 	  break;
 
+	case DW_EH_PE_udata1:
+	  result = *p;
+	  p += 1;
+	  break;
 	case DW_EH_PE_udata2:
 	  result = u->u2;
 	  p += 2;
Index: libgcc/unwind-compact.c
===================================================================
--- libgcc/unwind-compact.c	(revision 0)
+++ libgcc/unwind-compact.c	(revision 0)
@@ -0,0 +1,111 @@
+/* Copyright (C) 2012-2015 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   Under Section 7 of GPL version 3, you are granted additional
+   permissions described in the GCC Runtime Library Exception, version
+   3.1, as published by the Free Software Foundation.
+
+   You should have received a copy of the GNU General Public License and
+   a copy of the GCC Runtime Library Exception along with this program;
+   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "tconfig.h"
+#include "tsystem.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "dwarf2.h"
+#include "unwind.h"
+#include "unwind-dw2-fde.h"
+#include "unwind-compact.h"
+
+typedef unsigned int uint32_t  __attribute__ ((mode (SI)));
+
+#ifdef MD_HAVE_COMPACT_EH
+/* Search a compact (v2) eh_frame_hdr table.  */
+
+struct compact_eh_index {
+    sword fn;
+    sword data;
+} __attribute__((packed, aligned(4)));
+
+static _Unwind_Ptr
+decode_index_ptr (const sword *p)
+{
+  return (*p & ~1) + (_Unwind_Ptr) p;
+}
+
+enum compact_entry_type
+_Unwind_Search_Compact_Eh_Hdr (void *pc, const unsigned char *hdr,
+			       struct compact_eh_bases *bases)
+{
+  int lo, hi, mid;
+  _Unwind_Ptr p;
+  _Unwind_Ptr nrec;
+  const struct compact_eh_index *ind;
+
+  bases->eh_encoding = hdr[1];
+  nrec = *(const uint32_t *)(hdr + 4);
+  if (nrec == 0)
+    return CET_not_found;
+
+  ind = (const struct compact_eh_index *)(hdr + 8);
+
+  lo = 0;
+  hi = nrec - 1;
+  while (1)
+    {
+      mid = (lo + hi) / 2;
+      p = decode_index_ptr (&ind[mid].fn);
+      if ((_Unwind_Ptr)pc < p)
+	{
+	  if (mid == 0)
+	    return CET_not_found;
+	  hi = mid - 1;
+	}
+      else
+	{
+	  if (mid == hi)
+	    break;
+	  p = decode_index_ptr (&ind[mid + 1].fn);
+	  if ((_Unwind_Ptr)pc < p)
+	    break;
+	  lo = mid + 1;
+	}
+    }
+
+  p = decode_index_ptr (&ind[mid].fn);
+  bases->func = (void *)p;
+  if ((ind[mid].fn & 1) == 0)
+    {
+      bases->entry = &ind[mid].data;
+      /* If we hit the terminating CANTUNWIND entry then assume
+         we're looking in the wrong table.  */
+      if (mid == (int)nrec - 1 && ind[mid].data == 0x015d5d01)
+	return CET_not_found;
+
+      return CET_inline;
+    }
+  else
+    {
+      p = decode_index_ptr (&ind[mid].data);
+      bases->entry = (const void *)p;
+      if (ind[mid].data & 1)
+	return CET_FDE;
+      else
+	return CET_outline;
+    }
+}
+
+#endif /* MD_HAVE_COMPACT_EH */
Index: libgcc/unwind-dw2-fde.c
===================================================================
--- libgcc/unwind-dw2-fde.c	(revision 231238)
+++ libgcc/unwind-dw2-fde.c	(working copy)
@@ -23,7 +23,8 @@ a copy of the GCC Runtime Library Exception along
 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 <http://www.gnu.org/licenses/>.  */
 
-#ifndef _Unwind_Find_FDE
+#ifndef _Unwind_Find_registered_Index
+#define _Unwind_Find_registered_Index _Unwind_Find_Index
 #include "tconfig.h"
 #include "tsystem.h"
 #include "coretypes.h"
@@ -35,7 +36,10 @@ see the files COPYING3 and COPYING.RUNTIME respect
 #include "unwind-pe.h"
 #include "unwind-dw2-fde.h"
 #include "gthr.h"
+#ifdef MD_HAVE_COMPACT_EH
+#include "unwind-compact.h"
 #endif
+#endif
 
 /* The unseen_objects list contains objects that have been registered
    but not yet categorized in any way.  The seen_objects list has had
@@ -71,22 +75,20 @@ static __gthread_mutex_t object_mutex;
 #endif
 #endif
 
-/* Called from crtbegin.o to register the unwind info for an object.  */
+/* Initialize OB and add to list of objects.  */
 
-void
-__register_frame_info_bases (const void *begin, struct object *ob,
-			     void *tbase, void *dbase)
+static void
+__register_frame_info_1 (const void *begin, struct object *ob,
+			  void *tbase, void *dbase, int header)
 {
-  /* If .eh_frame is empty, don't register at all.  */
-  if ((const uword *) begin == 0 || *(const uword *) begin == 0)
-    return;
-
   ob->pc_begin = (void *)-1;
   ob->tbase = tbase;
   ob->dbase = dbase;
   ob->u.single = begin;
   ob->s.i = 0;
   ob->s.b.encoding = DW_EH_PE_omit;
+  ob->s.b.header = header;
+  ob->s.b.mixed_encoding = header;
 #ifdef DWARF2_OBJECT_END_PTR_EXTENSION
   ob->fde_end = NULL;
 #endif
@@ -100,7 +102,35 @@ static __gthread_mutex_t object_mutex;
   __gthread_mutex_unlock (&object_mutex);
 }
 
+/* Called from crtbegin.o to register the unwind info for an object.  */
+
 void
+__register_frame_info_header_bases (const void *begin, struct object *ob,
+				    void *tbase, void *dbase)
+{
+  /* Only register compact EH frame headers.  */
+  if (begin == NULL || *(const char *) begin != 2)
+    return;
+
+#ifdef MD_HAVE_COMPACT_EH
+  __register_frame_info_1 (begin, ob, tbase, dbase, 1);
+#endif
+}
+
+/* Called from crtbegin.o to register the unwind info for an object.  */
+
+void
+__register_frame_info_bases (const void *begin, struct object *ob,
+			     void *tbase, void *dbase)
+{
+  /* If .eh_frame is empty, don't register at all.  */
+  if ((const uword *) begin == 0 || *(const uword *) begin == 0)
+    return;
+
+  __register_frame_info_1 (begin, ob, tbase, dbase, 0);
+}
+
+void
 __register_frame_info (const void *begin, struct object *ob)
 {
   __register_frame_info_bases (begin, ob, 0, 0);
@@ -951,9 +981,25 @@ binary_search_mixed_encoding_fdes (struct object *
   return NULL;
 }
 
-static const fde *
-search_object (struct object* ob, void *pc)
+static enum compact_entry_type
+search_object (struct object* ob, void *pc, struct compact_eh_bases *bases)
 {
+  const fde *f = NULL;
+
+#ifdef MD_HAVE_COMPACT_EH
+  if (ob->s.b.header)
+    {
+      const unsigned char *hdr = (const unsigned char *) ob->u.single;
+      if (ob->pc_begin == (void *) -1)
+	{
+	  const sword *first = (const sword *) (hdr + 8);
+	  ob->pc_begin = (void *)((*first & ~1) + (_Unwind_Ptr) first);
+	}
+
+      return _Unwind_Search_Compact_Eh_Hdr (pc, hdr, bases);
+    }
+#endif
+
   /* If the data hasn't been sorted, try to do this now.  We may have
      more memory available than last time we tried.  */
   if (! ob->s.b.sorted)
@@ -964,17 +1010,17 @@ binary_search_mixed_encoding_fdes (struct object *
 	 that we've not processed this object before.  A quick range
 	 check is in order.  */
       if (pc < ob->pc_begin)
-	return NULL;
+	return CET_not_found;
     }
 
   if (ob->s.b.sorted)
     {
       if (ob->s.b.mixed_encoding)
-	return binary_search_mixed_encoding_fdes (ob, pc);
+	f = binary_search_mixed_encoding_fdes (ob, pc);
       else if (ob->s.b.encoding == DW_EH_PE_absptr)
-	return binary_search_unencoded_fdes (ob, pc);
+	f = binary_search_unencoded_fdes (ob, pc);
       else
-	return binary_search_single_encoding_fdes (ob, pc);
+	f = binary_search_single_encoding_fdes (ob, pc);
     }
   else
     {
@@ -984,22 +1030,24 @@ binary_search_mixed_encoding_fdes (struct object *
 	  fde **p;
 	  for (p = ob->u.array; *p ; p++)
 	    {
-	      const fde *f = linear_search_fdes (ob, *p, pc);
+	      f = linear_search_fdes (ob, *p, pc);
 	      if (f)
-		return f;
+		break;
 	    }
-	  return NULL;
 	}
       else
-	return linear_search_fdes (ob, ob->u.single, pc);
+	f = linear_search_fdes (ob, ob->u.single, pc);
     }
+
+  bases->entry = f;
+  return f ? CET_FDE : CET_not_found;
 }
 
-const fde *
-_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
+enum compact_entry_type
+_Unwind_Find_registered_Index (void *pc, struct compact_eh_bases *bases)
 {
   struct object *ob;
-  const fde *f = NULL;
+  enum compact_entry_type t = CET_not_found;
 
   init_object_mutex_once ();
   __gthread_mutex_lock (&object_mutex);
@@ -1010,8 +1058,8 @@ binary_search_mixed_encoding_fdes (struct object *
   for (ob = seen_objects; ob; ob = ob->next)
     if (pc >= ob->pc_begin)
       {
-	f = search_object (ob, pc);
-	if (f)
+	t = search_object (ob, pc, bases);
+	if (t != CET_not_found)
 	  goto fini;
 	break;
       }
@@ -1022,7 +1070,7 @@ binary_search_mixed_encoding_fdes (struct object *
       struct object **p;
 
       unseen_objects = ob->next;
-      f = search_object (ob, pc);
+      t = search_object (ob, pc, bases);
 
       /* Insert the object into the classified list.  */
       for (p = &seen_objects; *p ; p = &(*p)->next)
@@ -1031,21 +1079,26 @@ binary_search_mixed_encoding_fdes (struct object *
       ob->next = *p;
       *p = ob;
 
-      if (f)
+      if (t != CET_not_found)
 	goto fini;
     }
 
  fini:
   __gthread_mutex_unlock (&object_mutex);
 
-  if (f)
+  if (t == CET_not_found)
+    return CET_not_found;
+
+  bases->tbase = ob->tbase;
+  bases->dbase = ob->dbase;
+
+  if (t == CET_FDE)
     {
+      const fde *f;
       int encoding;
       _Unwind_Ptr func;
 
-      bases->tbase = ob->tbase;
-      bases->dbase = ob->dbase;
-
+      f = bases->entry;
       encoding = ob->s.b.encoding;
       if (ob->s.b.mixed_encoding)
 	encoding = get_fde_encoding (f);
@@ -1054,5 +1107,21 @@ binary_search_mixed_encoding_fdes (struct object *
       bases->func = (void *) func;
     }
 
-  return f;
+  return t;
 }
+
+const fde *
+_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases)
+{
+  enum compact_entry_type type;
+  struct compact_eh_bases data;
+
+  type = _Unwind_Find_Index (pc, &data);
+  if (type != CET_FDE)
+    return NULL;
+
+  bases->tbase = data.tbase;
+  bases->dbase = data.dbase;
+  bases->func = data.func;
+  return (const fde *) data.entry;
+}
Index: libgcc/unwind-compact.h
===================================================================
--- libgcc/unwind-compact.h	(revision 0)
+++ libgcc/unwind-compact.h	(revision 0)
@@ -0,0 +1,39 @@
+/* Copyright (C) 2012-2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_UNWIND_COMPACT_H
+#define GCC_UNWIND_COMPACT_H
+
+enum compact_entry_type _Unwind_Search_Compact_Eh_Hdr (void *,
+    const unsigned char *, struct compact_eh_bases *);
+
+_Unwind_Reason_Code __gnu_compact_pr1 (int, _Unwind_Action,
+		  _Unwind_Exception_Class, struct _Unwind_Exception *,
+		  struct _Unwind_Context *);
+
+_Unwind_Reason_Code __gnu_compact_pr2 (int, _Unwind_Action,
+		  _Unwind_Exception_Class, struct _Unwind_Exception *,
+		  struct _Unwind_Context *);
+
+#define EH_FRAME_HEADER_VERSION 2
+#endif
+
Index: libgcc/unwind-dw2-fde.h
===================================================================
--- libgcc/unwind-dw2-fde.h	(revision 231238)
+++ libgcc/unwind-dw2-fde.h	(working copy)
@@ -53,10 +53,11 @@ struct object
       unsigned long sorted : 1;
       unsigned long from_array : 1;
       unsigned long mixed_encoding : 1;
+      unsigned long header : 1;
       unsigned long encoding : 8;
       /* ??? Wish there was an easy way to detect a 64-bit host here;
 	 we've got 32 bits left to play with...  */
-      unsigned long count : 21;
+      unsigned long count : 20;
     } b;
     size_t i;
   } s;
@@ -87,9 +88,12 @@ struct dwarf_eh_bases
   void *tbase;
   void *dbase;
   void *func;
+  unsigned char eh_encoding;
 };
 
 
+extern void __register_frame_info_header_bases (const void *, struct object *,
+						void *, void *);
 extern void __register_frame_info_bases (const void *, struct object *,
 					 void *, void *);
 extern void __register_frame_info (const void *, struct object *);
@@ -165,6 +169,24 @@ next_fde (const fde *f)
 
 extern const fde * _Unwind_Find_FDE (void *, struct dwarf_eh_bases *);
 
+struct compact_eh_bases {
+    void *tbase;
+    void *dbase;
+    void *func;
+    const void *entry;
+    unsigned char eh_encoding;
+};
+
+enum compact_entry_type {
+    CET_not_found,
+    CET_FDE,
+    CET_inline,
+    CET_outline
+};
+
+extern enum compact_entry_type _Unwind_Find_Index (void *,
+						   struct compact_eh_bases *);
+
 static inline int
 last_fde (struct object *obj __attribute__ ((__unused__)), const fde *f)
 {
Index: libgcc/crtstuff.c
===================================================================
--- libgcc/crtstuff.c	(revision 231238)
+++ libgcc/crtstuff.c	(working copy)
@@ -182,6 +182,9 @@ extern void __register_frame_info (const void *, s
 extern void __register_frame_info_bases (const void *, struct object *,
 					 void *, void *)
 				  TARGET_ATTRIBUTE_WEAK;
+extern void __register_frame_info_header_bases (const void *, struct object *,
+					 void *, void *)
+				  TARGET_ATTRIBUTE_WEAK;
 extern void *__deregister_frame_info (const void *)
 				     TARGET_ATTRIBUTE_WEAK;
 extern void *__deregister_frame_info_bases (const void *)
@@ -264,6 +267,10 @@ STATIC func_ptr __DTOR_LIST__[1]
 STATIC EH_FRAME_SECTION_CONST char __EH_FRAME_BEGIN__[]
      __attribute__((section(__LIBGCC_EH_FRAME_SECTION_NAME__), aligned(4)))
      = { };
+
+#ifdef MD_HAVE_COMPACT_EH
+extern char __GNU_EH_FRAME_HDR[] TARGET_ATTRIBUTE_WEAK __attribute__((aligned(4)));
+#endif /* MD_HAVE_COMPACT_EH */
 #endif /* USE_EH_FRAME_REGISTRY */
 
 #ifdef __LIBGCC_JCR_SECTION_NAME__
@@ -418,6 +425,12 @@ __do_global_dtors_aux (void)
 
 #ifdef USE_EH_FRAME_REGISTRY
 #ifdef CRT_GET_RFIB_DATA
+#ifdef MD_HAVE_COMPACT_EH
+  if (__register_frame_info_header_bases && __GNU_EH_FRAME_HDR &&
+      __GNU_EH_FRAME_HDR[0] > 1)
+    __deregister_frame_info_bases (__GNU_EH_FRAME_HDR);
+  else
+#endif /* MD_HAVE_COMPACT_EH */
   /* If we used the new __register_frame_info_bases interface,
      make sure that we deregister from the same place.  */
   if (__deregister_frame_info_bases)
@@ -464,6 +477,13 @@ frame_dummy (void)
   void *tbase, *dbase;
   tbase = 0;
   CRT_GET_RFIB_DATA (dbase);
+#ifdef MD_HAVE_COMPACT_EH
+  if (__register_frame_info_header_bases && __GNU_EH_FRAME_HDR &&
+      __GNU_EH_FRAME_HDR[0] > 1)
+    __register_frame_info_header_bases (__GNU_EH_FRAME_HDR, &object,
+					tbase, dbase);
+  else
+#endif /* MD_HAVE_COMPACT_EH */
   if (__register_frame_info_bases)
     __register_frame_info_bases (__EH_FRAME_BEGIN__, &object, tbase, dbase);
 #else
Index: libgcc/unwind-generic.h
===================================================================
--- libgcc/unwind-generic.h	(revision 231238)
+++ libgcc/unwind-generic.h	(working copy)
@@ -174,6 +174,8 @@ extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_C
 extern _Unwind_Ptr _Unwind_GetIPInfo (struct _Unwind_Context *, int *);
 extern void _Unwind_SetIP (struct _Unwind_Context *, _Unwind_Ptr);
 
+extern unsigned char _Unwind_GetEhEncoding (struct _Unwind_Context *);
+
 /* @@@ Retrieve the CFA of the given context.  */
 extern _Unwind_Word _Unwind_GetCFA (struct _Unwind_Context *);
 
Index: libgcc/config/t-eh-dw2-dip
===================================================================
--- libgcc/config/t-eh-dw2-dip	(revision 231238)
+++ libgcc/config/t-eh-dw2-dip	(working copy)
@@ -1,3 +1,3 @@
 # Use unwind-dw2-fde-dip.
-LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde-dip.c \
-  $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c
+LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-compact.c \
+  $(srcdir)/unwind-dw2-fde-dip.c $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c
Index: libgcc/config/mips/mips16.S
===================================================================
--- libgcc/config/mips/mips16.S	(revision 231238)
+++ libgcc/config/mips/mips16.S	(working copy)
@@ -624,6 +624,18 @@ CALL_STUB_NO_RET (__mips16_call_stub_10, 10)
    being called is 16 bits, in which case the copy is unnecessary;
    however, it's faster to always do the copy.  */
 
+#ifdef __GNU_COMPACT_EH__
+#define CALL_STUB_RET(NAME, CODE, MODE)	\
+STARTFN (NAME);				\
+	move    $18,$31;		\
+	STUB_ARGS_##CODE;		\
+	.set    noreorder;		\
+	jalr    $2;			\
+	move    $25,$2;			\
+	.set    reorder;		\
+	MOVE_##MODE##_RET (f, $18);	\
+	ENDFN (NAME)
+#else
 #define CALL_STUB_RET(NAME, CODE, MODE)					\
 CE_STARTFN (NAME);							\
 	.cfi_startproc;							\
@@ -642,6 +654,7 @@ CE_STARTFN (NAME);							\
 	MOVE_##MODE##_RET (f, $18);					\
 	.cfi_endproc;							\
 	CE_ENDFN (NAME)
+#endif
 
 /* First, instantiate the single-float set.  */
 
Index: libgcc/config/mips/linux-unwind.h
===================================================================
--- libgcc/config/mips/linux-unwind.h	(revision 231238)
+++ libgcc/config/mips/linux-unwind.h	(working copy)
@@ -26,6 +26,8 @@ see the files COPYING3 and COPYING.RUNTIME respect
 /* Do code reading to identify a signal frame, and set the frame
    state data appropriately.  See unwind-dw2.c for the structs.  */
 
+#include "config/mips/mips-unwind.h"
+
 #include <signal.h>
 #include <asm/unistd.h>
 
Index: libgcc/config/mips/mips-unwind.h
===================================================================
--- libgcc/config/mips/mips-unwind.h	(revision 0)
+++ libgcc/config/mips/mips-unwind.h	(revision 0)
@@ -0,0 +1,195 @@
+/* Compact EH unwinding support for MIPS.
+   Copyright (C) 2012-2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#ifdef MD_HAVE_COMPACT_EH
+
+#define DWARF_SP_REGNO 29
+
+#if _MIPS_SIM == _ABIO32
+#define MIPS_EH_STACK_ALIGN 8
+#else
+#define MIPS_EH_STACK_ALIGN 16
+#endif
+
+#define VRF_0 32
+
+/* Record the push of a register in FrameState FS.  */
+
+static int
+record_push (_Unwind_FrameState *fs, int reg, int offset)
+{
+  int idx = DWARF_REG_TO_UNWIND_COLUMN (reg);
+
+  offset -= dwarf_reg_size_table[idx];
+  fs->regs.reg[idx].how = REG_SAVED_OFFSET;
+  fs->regs.reg[idx].loc.offset = offset;
+  return offset;
+}
+
+/* Adjust the cfa_offset by value VAL.  Record the new cfa_offset in
+   FrameState FS.  */
+
+static void
+record_cfa_adjustment (_Unwind_FrameState *fs, _uleb128_t val)
+{
+  int i;
+  fs->regs.cfa_offset += val;
+  /* In case we see an adjustment after pushes, it means that
+     the registers aren't saved at the top of the frame, probably
+     due to a varargs save area.  Adjust the recorded offsets.  */
+  for (i = 0; i < DWARF_FRAME_REGISTERS + 1; i++)
+    if (fs->regs.reg[i].how == REG_SAVED_OFFSET)
+      fs->regs.reg[i].loc.offset -= val;
+}
+
+/* Process the frame unwinding opcodes.  */
+
+static _Unwind_Reason_Code
+md_unwind_compact (struct _Unwind_Context *context ATTRIBUTE_UNUSED,
+		   _Unwind_FrameState *fs, const unsigned char **pp)
+{
+  unsigned char op;
+  _uleb128_t val;
+  int push_offset;
+  int i;
+  int n;
+  const unsigned char *p = *pp;
+
+  push_offset = 0;
+  fs->regs.cfa_how = CFA_REG_OFFSET;
+  fs->regs.cfa_reg = STACK_POINTER_REGNUM;
+  fs->regs.cfa_offset = 0;
+  fs->retaddr_column = 31;
+
+  while (1)
+    {
+      op = *(p++);
+      switch (op)
+	{
+	case 0 ... 0x39:
+	  /* Increment stack pointer.  */
+	  record_cfa_adjustment (fs, (op + 1) * MIPS_EH_STACK_ALIGN);
+	  break;
+
+        case 0x40 ... 0x47:
+	  /* Push VR[16] to VR[16+x] and VR[31] */
+	  push_offset = record_push (fs, 31, push_offset);
+	  for (i = op & 7; i >= 0; i--)
+	    push_offset = record_push (fs, 16 + i, push_offset);
+	  break;
+
+	case 0x48 ... 0x4f:
+	  /* Push VR[16] to VR[16+x], VR[30] and VR[31] */
+	  push_offset = record_push (fs, 31, push_offset);
+	  push_offset = record_push (fs, 30, push_offset);
+	  for (i = op & 7; i >= 0; i--)
+	    push_offset = record_push (fs, 16 + i, push_offset);
+	  break;
+
+	case 0x50 ... 0x57:
+	  /* Restore stack pointer from frame pointer */
+	  fs->regs.cfa_reg = (op & 7) + 16;
+	  fs->regs.cfa_offset = 0;
+	  break;
+
+	case 0x58:
+	  /* Large SP increment.  */
+	  p = read_uleb128 (p, &val);
+	  record_cfa_adjustment (fs, (val + 129) * MIPS_EH_STACK_ALIGN);
+	  break;
+
+	case 0x59:
+	  /* Push VR[x] to VR[x+y] */
+	  op = *(p++);
+	  n = op >> 3;
+	  for (i = op & 7; i >= 0; i--)
+	    push_offset = record_push (fs, n + i, push_offset);
+	  break;
+
+	case 0x5a:
+	  /* Push VRF[x] to VRF[x+y] */
+	  op = *(p++);
+	  n = (op >> 3) + VRF_0;
+	  for (i = op & 7; i >= 0; i--)
+	    push_offset = record_push (fs, n + i, push_offset);
+	  break;
+
+	case 0x5b:
+	  /* Restore the CFA to stack pointer.  */
+	  fs->regs.cfa_reg = STACK_POINTER_REGNUM;
+	  fs->regs.cfa_offset = 0;
+	  break;
+
+	case 0x5c:
+	  /* Finish.  */
+	  *pp = p;
+	  return _URC_NO_REASON;
+
+	case 0x5d:
+	  /* No unwind.  */
+	  return _URC_END_OF_STACK;
+
+	case 0x5e:
+	  /* Restore SP from VR[30] */
+	  fs->regs.cfa_reg = 30;
+	  fs->regs.cfa_offset = 0;
+	  break;
+
+	case 0x5f:
+	  /* NOP */
+	  break;
+
+	case 0x60 ... 0x6b:
+	  /* Push VRF[20] to VRF[20 + x] */
+	  for (i = op & 0xf; i >= 0; i--)
+	    push_offset = record_push (fs, VRF_0 + 20 + i, push_offset);
+	  break;
+
+	case 0x6c ... 0x6f:
+	  /* MIPS16 push VR[16], VR[17], VR[18+x]-VR[23], VR[31] */
+	  push_offset = record_push (fs, 31, push_offset);
+	  for (i = 23; i >= 18 + (op & 3); i--)
+	    push_offset = record_push (fs, i, push_offset);
+	  push_offset = record_push (fs, 17, push_offset);
+	  push_offset = record_push (fs, 16, push_offset);
+	  break;
+
+	case 0x70 ... 0x7f:    
+	  /* Push VR[16] to VR[16+x], VR[28], VR[31]
+	     and optionally VR[30].  */
+	  push_offset = record_push (fs, 31, push_offset);
+	  if (op & 0x08)
+	    push_offset = record_push (fs, 30, push_offset);
+	  push_offset = record_push (fs, 28, push_offset);
+	  for (i = op & 7; i >= 0; i--)
+	    push_offset = record_push (fs, 16 + i, push_offset);
+	  break;
+
+	default:
+	  return _URC_FATAL_PHASE1_ERROR;
+        }
+    }
+}
+
+#endif /* MD_HAVE_COMPACT_EH */

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2015-12-13 22:12 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-07-30 21:07 [RFA] Compact EH Patch Moore, Catherine
2015-08-14 19:58 ` Moore, Catherine
2015-09-09 20:45 ` Jason Merrill
2015-09-09 23:53   ` Richard Henderson
2015-09-14 19:32     ` Moore, Catherine
2015-09-18 19:34 ` Richard Henderson
2015-10-05 23:14   ` Moore, Catherine
2015-10-06 16:12     ` Richard Henderson
2015-11-25 17:13   ` Moore, Catherine
2015-12-01 21:32     ` Moore, Catherine
2015-12-01 21:33     ` Jason Merrill
2015-12-02 13:39       ` Jonathan Wakely
2015-12-13 22:12   ` Moore, Catherine
2015-10-28 16:44 ` Matthew Fortune

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).