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

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).