OSDN Git Service

1999-01-19 Vladimir N. Makarov <vmakarov@cygnus.com>
[pf3gnuchains/gcc-fork.git] / gcc / libgcc2.c
index e5ad420..b06b771 100644 (file)
@@ -31,6 +31,17 @@ Boston, MA 02111-1307, USA.  */
    do not apply.  */
 
 #include "tconfig.h"
+
+/* We disable this when inhibit_libc, so that gcc can still be built without
+   needing header files first.  */
+/* ??? This is not a good solution, since prototypes may be required in
+   some cases for correct code.  See also frame.c.  */
+#ifndef inhibit_libc
+/* fixproto guarantees these system headers exist. */
+#include <stdlib.h>
+#include <unistd.h>
+#endif
+
 #include "machmode.h"
 #include "defaults.h" 
 #ifndef L_trampoline
@@ -61,6 +72,10 @@ Boston, MA 02111-1307, USA.  */
 #define LIBGCC2_WORDS_BIG_ENDIAN WORDS_BIG_ENDIAN
 #endif
 
+#ifndef LIBGCC2_LONG_DOUBLE_TYPE_SIZE
+#define LIBGCC2_LONG_DOUBLE_TYPE_SIZE LONG_DOUBLE_TYPE_SIZE
+#endif
+
 /* In the first part of this file, we are interfacing to calls generated
    by the compiler itself.  These calls pass values into these routines
    which have very specific modes (rather than very specific types), and
@@ -79,10 +94,10 @@ typedef unsigned int UDItype        __attribute__ ((mode (DI)));
 typedef        float SFtype    __attribute__ ((mode (SF)));
 typedef                float DFtype    __attribute__ ((mode (DF)));
 
-#if LONG_DOUBLE_TYPE_SIZE == 96
+#if LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 96
 typedef                float XFtype    __attribute__ ((mode (XF)));
 #endif
-#if LONG_DOUBLE_TYPE_SIZE == 128
+#if LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 128
 typedef                float TFtype    __attribute__ ((mode (TF)));
 #endif
 
@@ -133,10 +148,10 @@ typedef union
 
 extern DItype __fixunssfdi (SFtype a);
 extern DItype __fixunsdfdi (DFtype a);
-#if LONG_DOUBLE_TYPE_SIZE == 96
+#if LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 96
 extern DItype __fixunsxfdi (XFtype a);
 #endif
-#if LONG_DOUBLE_TYPE_SIZE == 128
+#if LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 128
 extern DItype __fixunstfdi (TFtype a);
 #endif
 \f
@@ -391,8 +406,13 @@ __udiv_w_sdiv (USItype *rp, USItype a1, USItype a0, USItype d)
 #else
 /* If sdiv_qrnnd doesn't exist, define dummy __udiv_w_sdiv.  */
 USItype
-__udiv_w_sdiv (USItype *rp, USItype a1, USItype a0, USItype d)
-{}
+__udiv_w_sdiv (USItype *rp __attribute__ ((__unused__)),
+              USItype a1 __attribute__ ((__unused__)),
+              USItype a0 __attribute__ ((__unused__)),
+              USItype d __attribute__ ((__unused__)))
+{
+  return 0;
+}
 #endif
 #endif
 \f
@@ -752,7 +772,7 @@ __ucmpdi2 (DItype a, DItype b)
 }
 #endif
 \f
-#if defined(L_fixunstfdi) && (LONG_DOUBLE_TYPE_SIZE == 128)
+#if defined(L_fixunstfdi) && (LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 128)
 #define WORD_SIZE (sizeof (SItype) * BITS_PER_UNIT)
 #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
 
@@ -784,7 +804,7 @@ __fixunstfdi (TFtype a)
 }
 #endif
 
-#if defined(L_fixtfdi) && (LONG_DOUBLE_TYPE_SIZE == 128)
+#if defined(L_fixtfdi) && (LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 128)
 DItype
 __fixtfdi (TFtype a)
 {
@@ -794,7 +814,7 @@ __fixtfdi (TFtype a)
 }
 #endif
 
-#if defined(L_fixunsxfdi) && (LONG_DOUBLE_TYPE_SIZE == 96)
+#if defined(L_fixunsxfdi) && (LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 96)
 #define WORD_SIZE (sizeof (SItype) * BITS_PER_UNIT)
 #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
 
@@ -826,7 +846,7 @@ __fixunsxfdi (XFtype a)
 }
 #endif
 
-#if defined(L_fixxfdi) && (LONG_DOUBLE_TYPE_SIZE == 96)
+#if defined(L_fixxfdi) && (LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 96)
 DItype
 __fixxfdi (XFtype a)
 {
@@ -924,7 +944,7 @@ __fixsfdi (SFtype a)
 }
 #endif
 
-#if defined(L_floatdixf) && (LONG_DOUBLE_TYPE_SIZE == 96)
+#if defined(L_floatdixf) && (LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 96)
 #define WORD_SIZE (sizeof (SItype) * BITS_PER_UNIT)
 #define HIGH_HALFWORD_COEFF (((UDItype) 1) << (WORD_SIZE / 2))
 #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
@@ -933,21 +953,17 @@ XFtype
 __floatdixf (DItype u)
 {
   XFtype d;
-  SItype negate = 0;
 
-  if (u < 0)
-    u = -u, negate = 1;
-
-  d = (USItype) (u >> WORD_SIZE);
+  d = (SItype) (u >> WORD_SIZE);
   d *= HIGH_HALFWORD_COEFF;
   d *= HIGH_HALFWORD_COEFF;
   d += (USItype) (u & (HIGH_WORD_COEFF - 1));
 
-  return (negate ? -d : d);
+  return d;
 }
 #endif
 
-#if defined(L_floatditf) && (LONG_DOUBLE_TYPE_SIZE == 128)
+#if defined(L_floatditf) && (LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 128)
 #define WORD_SIZE (sizeof (SItype) * BITS_PER_UNIT)
 #define HIGH_HALFWORD_COEFF (((UDItype) 1) << (WORD_SIZE / 2))
 #define HIGH_WORD_COEFF (((UDItype) 1) << WORD_SIZE)
@@ -956,17 +972,13 @@ TFtype
 __floatditf (DItype u)
 {
   TFtype d;
-  SItype negate = 0;
-
-  if (u < 0)
-    u = -u, negate = 1;
 
-  d = (USItype) (u >> WORD_SIZE);
+  d = (SItype) (u >> WORD_SIZE);
   d *= HIGH_HALFWORD_COEFF;
   d *= HIGH_HALFWORD_COEFF;
   d += (USItype) (u & (HIGH_WORD_COEFF - 1));
 
-  return (negate ? -d : d);
+  return d;
 }
 #endif
 
@@ -979,17 +991,13 @@ DFtype
 __floatdidf (DItype u)
 {
   DFtype d;
-  SItype negate = 0;
-
-  if (u < 0)
-    u = -u, negate = 1;
 
-  d = (USItype) (u >> WORD_SIZE);
+  d = (SItype) (u >> WORD_SIZE);
   d *= HIGH_HALFWORD_COEFF;
   d *= HIGH_HALFWORD_COEFF;
   d += (USItype) (u & (HIGH_WORD_COEFF - 1));
 
-  return (negate ? -d : d);
+  return d;
 }
 #endif
 
@@ -1034,10 +1042,6 @@ __floatdisf (DItype u)
      so that we don't lose any of the precision of the high word
      while multiplying it.  */
   DFtype f;
-  SItype negate = 0;
-
-  if (u < 0)
-    u = -u, negate = 1;
 
   /* Protect against double-rounding error.
      Represent any low-order bits, that might be truncated in DFmode,
@@ -1049,22 +1053,23 @@ __floatdisf (DItype u)
       && DF_SIZE > (DI_SIZE - DF_SIZE + SF_SIZE))
     {
 #define REP_BIT ((USItype) 1 << (DI_SIZE - DF_SIZE))
-      if (u >= ((UDItype) 1 << DF_SIZE))
+      if (! (- ((DItype) 1 << DF_SIZE) < u
+            && u < ((DItype) 1 << DF_SIZE)))
        {
          if ((USItype) u & (REP_BIT - 1))
            u |= REP_BIT;
        }
     }
-  f = (USItype) (u >> WORD_SIZE);
+  f = (SItype) (u >> WORD_SIZE);
   f *= HIGH_HALFWORD_COEFF;
   f *= HIGH_HALFWORD_COEFF;
   f += (USItype) (u & (HIGH_WORD_COEFF - 1));
 
-  return (SFtype) (negate ? -f : f);
+  return (SFtype) f;
 }
 #endif
 
-#if defined(L_fixunsxfsi) && LONG_DOUBLE_TYPE_SIZE == 96
+#if defined(L_fixunsxfsi) && LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 96
 /* Reenable the normal types, in case limits.h needs them.  */
 #undef char
 #undef short
@@ -1402,9 +1407,12 @@ __builtin_saveregs ()
 #undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch.  */
 #include <stdio.h>
 /* This is used by the `assert' macro.  */
+extern void __eprintf (const char *, const char *, unsigned int, const char *)
+  __attribute__ ((__noreturn__));
+
 void
 __eprintf (const char *string, const char *expression,
-          int line, const char *filename)
+          unsigned int line, const char *filename)
 {
   fprintf (stderr, string, expression, line, filename);
   fflush (stderr);
@@ -2156,7 +2164,7 @@ __bb_init_prg ()
       bb_hashbuckets = (struct bb_edge **) 
                    malloc (BB_BUCKETS * sizeof (struct bb_edge *));
       if (bb_hashbuckets)
-        bzero ((char *) bb_hashbuckets, BB_BUCKETS);
+        memset (bb_hashbuckets, 0, BB_BUCKETS * sizeof (struct bb_edge *));
     }
 
   if (bb_mode & 12)
@@ -2568,7 +2576,7 @@ __clear_cache (char *beg, char *end)
 
 /* Jump to a trampoline, loading the static chain address.  */
 
-#if defined(WINNT) && ! defined(__CYGWIN32__)
+#if defined(WINNT) && ! defined(__CYGWIN__)
 
 long getpagesize()
 {
@@ -2806,6 +2814,7 @@ cacheflush (char *beg, int size, int flag)
 #endif /* sony_news */
 #endif /* L_trampoline */
 \f
+#ifndef __CYGWIN__
 #ifdef L__main
 
 #include "gbl-ctors.h"
@@ -2887,6 +2896,7 @@ SYMBOL__MAIN ()
 #endif /* no HAS_INIT_SECTION or INVOKE__main */
 
 #endif /* L__main */
+#endif /* __CYGWIN__ */
 \f
 #ifdef L_ctors
 
@@ -2982,10 +2992,16 @@ exit (int status)
 #else /* No NEED_ATEXIT */
   __do_global_dtors ();
 #endif /* No NEED_ATEXIT */
-#endif
+#endif /* !defined (INIT_SECTION_ASM_OP) || !defined (OBJECT_FORMAT_ELF) */
+/* In gbl-ctors.h, ON_EXIT is defined if HAVE_ATEXIT is defined.  In
+   __bb_init_func and _bb_init_prg, __bb_exit_func is registered with
+   ON_EXIT if ON_EXIT is defined.  Thus we must not call __bb_exit_func here
+   if HAVE_ATEXIT is defined. */
+#ifndef HAVE_ATEXIT
 #ifndef inhibit_libc
   __bb_exit_func ();
 #endif
+#endif /* !HAVE_ATEXIT */
 #ifdef EXIT_BODY
   EXIT_BODY;
 #else
@@ -2994,9 +3010,17 @@ exit (int status)
   _exit (status);
 }
 
-#else
+#else /* ON_EXIT defined */
 int _exit_dummy_decl = 0;      /* prevent compiler & linker warnings */
-#endif
+
+# ifndef HAVE_ATEXIT
+/* Provide a fake for atexit() using ON_EXIT.  */
+int atexit (func_ptr func)
+{
+  return ON_EXIT (func, NULL);
+}
+# endif /* HAVE_ATEXIT */
+#endif /* ON_EXIT defined */
 
 #endif /* L_exit */
 \f
@@ -3006,6 +3030,8 @@ int _exit_dummy_decl = 0; /* prevent compiler & linker warnings */
 
 /* Shared exception handling support routines.  */
 
+extern void __default_terminate (void) __attribute__ ((__noreturn__));
+
 void
 __default_terminate ()
 {
@@ -3037,18 +3063,13 @@ __empty ()
 {
 }
 \f
-/* EH context structure. */
-
-struct eh_context
-{
-  void **dynamic_handler_chain;
-  /* This is language dependent part of the eh context. */
-  void *info;
-};
 
-/* This is a safeguard for dynamic handler chain. */
+/* Include definitions of EH context and table layout */
 
-static void *top_elt[2];
+#include "eh-common.h"
+#ifndef inhibit_libc
+#include <stdio.h>
+#endif
 
 /* Allocate and return a new EH context structure. */
 
@@ -3057,15 +3078,26 @@ extern void __throw ();
 static void *
 new_eh_context ()
 {
-  struct eh_context *eh = (struct eh_context *) malloc (sizeof *eh);
-  if (! eh)
+  struct eh_full_context {
+    struct eh_context c;
+    void *top_elt[2];
+  } *ehfc = (struct eh_full_context *) malloc (sizeof *ehfc);
+
+  if (! ehfc)
     __terminate ();
 
-  memset (eh, 0, sizeof *eh);
+  memset (ehfc, 0, sizeof *ehfc);
 
-  eh->dynamic_handler_chain = top_elt;
+  ehfc->c.dynamic_handler_chain = (void **) ehfc->top_elt;
 
-  return eh;
+  /* This should optimize out entirely.  This should always be true,
+     but just in case it ever isn't, don't allow bogus code to be
+     generated.  */
+
+  if ((void*)(&ehfc->c) != (void*)ehfc)
+    __terminate ();
+
+  return &ehfc->c;
 }
 
 #if __GTHREADS
@@ -3132,7 +3164,11 @@ eh_context_initialize ()
 #if __GTHREADS
 
   static __gthread_once_t once = __GTHREAD_ONCE_INIT;
-  if (__gthread_once (&once, eh_threads_initialize) == -1)
+  /* Make sure that get_eh_context does not point to us anymore.
+     Some systems have dummy thread routines in their libc that
+     return a success (Solaris 2.6 for example). */
+  if (__gthread_once (&once, eh_threads_initialize) != 0
+      || get_eh_context == &eh_context_initialize)
     {
       /* Use static version of EH context. */
       get_eh_context = &eh_context_static;
@@ -3153,10 +3189,17 @@ eh_context_initialize ()
 static struct eh_context *
 eh_context_static ()
 {
-  static struct eh_context *eh;
-  if (! eh)
-    eh = new_eh_context ();
-  return eh;
+  static struct eh_context eh;
+  static int initialized;
+  static void *top_elt[2];
+
+  if (! initialized)
+    {
+      initialized = 1;
+      memset (&eh, 0, sizeof eh);
+      eh.dynamic_handler_chain = top_elt;
+    }
+  return &eh;
 }
 
 #if __GTHREADS
@@ -3206,6 +3249,8 @@ __get_dynamic_handler_chain ()
    dynamic handler chain, and use longjmp to transfer back to the associated
    handler.  */
 
+extern void __sjthrow (void) __attribute__ ((__noreturn__));
+
 void
 __sjthrow ()
 {
@@ -3258,7 +3303,7 @@ __sjthrow ()
   /* We must call terminate if we try and rethrow an exception, when
      there is no exception currently active and when there are no
      handlers left.  */
-  if (! eh->info || (*dhc) == top_elt)
+  if (! eh->info || (*dhc)[0] == 0)
     __terminate ();
     
   /* Find the jmpbuf associated with the top element of the dynamic
@@ -3282,12 +3327,13 @@ __sjthrow ()
    then throw.  This is used to skip the first handler, and transfer
    control to the next handler in the dynamic handler stack.  */
 
+extern void __sjpopnthrow (void) __attribute__ ((__noreturn__));
+
 void
 __sjpopnthrow ()
 {
   struct eh_context *eh = (*get_eh_context) ();
   void ***dhc = &eh->dynamic_handler_chain;
-  void *jmpbuf;
   void (*func)(void *, int);
   void *arg;
   void ***cleanup;
@@ -3339,6 +3385,26 @@ __sjpopnthrow ()
 \f
 /* Support code for all exception region-based exception handling.  */
 
+int
+__eh_rtime_match (void *rtime)
+{
+  void *info;
+  __eh_matcher matcher;
+  void *ret;
+
+  info = *(__get_eh_info ());
+  matcher = ((__eh_info *)info)->match_function;
+  if (! matcher)
+    {
+#ifndef inhibit_libc
+      fprintf (stderr, "Internal Compiler Bug: No runtime type matcher.");
+#endif
+      return 0;
+    }
+  ret = (*matcher) (info, rtime, (void *)0);
+  return (ret != NULL);
+}
+
 /* This value identifies the place from which an exception is being
    thrown.  */
 
@@ -3348,11 +3414,24 @@ EH_TABLE_LOOKUP
 
 #else
 
-typedef struct exception_table {
-  void *start;
-  void *end;
-  void *exception_handler;
-} exception_table;
+#ifdef DWARF2_UNWIND_INFO
+
+
+/* Return the table version of an exception descriptor */
+
+short 
+__get_eh_table_version (exception_descriptor *table) 
+{
+  return table->lang.version;
+}
+
+/* Return the originating table language of an exception descriptor */
+
+short 
+__get_eh_table_language (exception_descriptor *table)
+{
+  return table->lang.language;
+}
 
 /* This routine takes a PC and a pointer to the exception region TABLE for
    its translation unit, and returns the address of the exception handler
@@ -3363,7 +3442,7 @@ typedef struct exception_table {
    an inner block.  */
 
 static void *
-find_exception_handler (void *pc, exception_table *table)
+old_find_exception_handler (void *pc, old_exception_table *table)
 {
   if (table)
     {
@@ -3371,27 +3450,106 @@ find_exception_handler (void *pc, exception_table *table)
       int best = -1;
 
       /* We can't do a binary search because the table isn't guaranteed
-        to be sorted from function to function.  */
-      for (pos = 0; table[pos].exception_handler != (void *) -1; ++pos)
-       {
-         if (table[pos].start <= pc && table[pos].end > pc)
-           {
-             /* This can apply.  Make sure it is at least as small as
-                the previous best.  */
-             if (best == -1 || (table[pos].end <= table[best].end
-                                && table[pos].start >= table[best].start))
-               best = pos;
-           }
-         /* But it is sorted by starting PC within a function.  */
-         else if (best >= 0 && table[pos].start > pc)
-           break;
-       }
+         to be sorted from function to function.  */
+      for (pos = 0; table[pos].start_region != (void *) -1; ++pos)
+        {
+          if (table[pos].start_region <= pc && table[pos].end_region > pc)
+            {
+              /* This can apply.  Make sure it is at least as small as
+                 the previous best.  */
+              if (best == -1 || (table[pos].end_region <= table[best].end_region
+                        && table[pos].start_region >= table[best].start_region))
+                best = pos;
+            }
+          /* But it is sorted by starting PC within a function.  */
+          else if (best >= 0 && table[pos].start_region > pc)
+            break;
+        }
       if (best != -1)
-       return table[best].exception_handler;
+        return table[best].exception_handler;
     }
 
   return (void *) 0;
 }
+
+/* find_exception_handler finds the correct handler, if there is one, to
+   handle an exception.
+   returns a pointer to the handler which controlled should be transferred
+   to, or NULL if there is nothing left.
+   Parameters:
+   PC - pc where the exception originates. If this is a rethrow, 
+        then this starts out as a pointer to the exception table
+       entry we wish to rethrow out of.
+   TABLE - exception table for the current module.
+   EH_INFO - eh info pointer for this exception.
+   RETHROW - 1 if this is a rethrow. (see incoming value of PC).
+   CLEANUP - returned flag indicating whether this is a cleanup handler.
+*/
+static void *
+find_exception_handler (void *pc, exception_descriptor *table, 
+                        __eh_info *eh_info, int rethrow, int *cleanup)
+{
+
+  void *retval = NULL;
+  *cleanup = 1;
+  if (table)
+    {
+      int pos = 0;
+      /* The new model assumed the table is sorted inner-most out so the
+         first region we find which matches is the correct one */
+
+      exception_table *tab = &(table->table[0]);
+
+      /* Subtract 1 from the PC to avoid hitting the next region */
+      if (rethrow) 
+        {
+          /* pc is actually the region table entry to rethrow out of */
+          pos = ((exception_table *) pc) - tab;
+          pc = ((exception_table *) pc)->end_region - 1;
+
+          /* The label is always on the LAST handler entry for a region, 
+             so we know the next entry is a different region, even if the
+             addresses are the same. Make sure its not end of table tho. */
+          if (tab[pos].start_region != (void *) -1)
+            pos++;
+        }
+      else
+        pc--;
+      
+      /* We can't do a binary search because the table is in inner-most
+         to outermost address ranges within functions */
+      for ( ; tab[pos].start_region != (void *) -1; pos++)
+        { 
+          if (tab[pos].start_region <= pc && tab[pos].end_region > pc)
+            {
+              if (tab[pos].match_info)
+                {
+                  __eh_matcher matcher = eh_info->match_function;
+                  /* match info but no matcher is NOT a match */
+                  if (matcher) 
+                    {
+                      void *ret = (*matcher)((void *) eh_info, 
+                                             tab[pos].match_info, table);
+                      if (ret) 
+                        {
+                          if (retval == NULL)
+                            retval = tab[pos].exception_handler;
+                          *cleanup = 0;
+                          break;
+                        }
+                    }
+                }
+              else
+                {
+                  if (retval == NULL)
+                    retval = tab[pos].exception_handler;
+                }
+            }
+        }
+    }
+  return retval;
+}
+#endif /* DWARF2_UNWIND_INFO */
 #endif /* EH_TABLE_LOOKUP */
 \f
 #ifdef DWARF2_UNWIND_INFO
@@ -3406,31 +3564,71 @@ find_exception_handler (void *pc, exception_table *table)
 
 typedef int ptr_type __attribute__ ((mode (pointer)));
 
-/* Get the value of register REG as saved in UDATA, where SUB_UDATA is a
+#ifdef INCOMING_REGNO
+/* Is the saved value for register REG in frame UDATA stored in a register
+   window in the previous frame?  */
+
+/* ??? The Sparc INCOMING_REGNO references TARGET_FLAT.  This allows us
+   to use the macro here.  One wonders, though, that perhaps TARGET_FLAT
+   compiled functions won't work with the frame-unwind stuff here.  
+   Perhaps the entireity of in_reg_window should be conditional on having
+   seen a DW_CFA_GNU_window_save?  */
+#define target_flags 0
+
+static int
+in_reg_window (int reg, frame_state *udata)
+{
+  if (udata->saved[reg] == REG_SAVED_REG)
+    return INCOMING_REGNO (reg) == reg;
+  if (udata->saved[reg] != REG_SAVED_OFFSET)
+    return 0;
+
+#ifdef STACK_GROWS_DOWNWARD
+  return udata->reg_or_offset[reg] > 0;
+#else
+  return udata->reg_or_offset[reg] < 0;
+#endif
+}
+#else
+static inline int in_reg_window (int reg, frame_state *udata) { return 0; }
+#endif /* INCOMING_REGNO */
+
+/* Get the address of register REG as saved in UDATA, where SUB_UDATA is a
    frame called by UDATA or 0.  */
 
-static void*
-get_reg (unsigned reg, frame_state *udata, frame_state *sub_udata)
+static word_type *
+get_reg_addr (unsigned reg, frame_state *udata, frame_state *sub_udata)
 {
+  while (udata->saved[reg] == REG_SAVED_REG)
+    {
+      reg = udata->reg_or_offset[reg];
+      if (in_reg_window (reg, udata))
+       {
+          udata = sub_udata;
+         sub_udata = NULL;
+       }
+    }
   if (udata->saved[reg] == REG_SAVED_OFFSET)
-    return (void *)(ptr_type)
-      *(word_type *)(udata->cfa + udata->reg_or_offset[reg]);
-  else if (udata->saved[reg] == REG_SAVED_REG && sub_udata)
-    return get_reg (udata->reg_or_offset[reg], sub_udata, 0);
+    return (word_type *)(udata->cfa + udata->reg_or_offset[reg]);
   else
     abort ();
 }
 
+/* Get the value of register REG as saved in UDATA, where SUB_UDATA is a
+   frame called by UDATA or 0.  */
+
+static inline void *
+get_reg (unsigned reg, frame_state *udata, frame_state *sub_udata)
+{
+  return (void *)(ptr_type) *get_reg_addr (reg, udata, sub_udata);
+}
+
 /* Overwrite the saved value for register REG in frame UDATA with VAL.  */
 
-static void
+static inline void
 put_reg (unsigned reg, void *val, frame_state *udata)
 {
-  if (udata->saved[reg] == REG_SAVED_OFFSET)
-    *(word_type *)(udata->cfa + udata->reg_or_offset[reg])
-      = (word_type)(ptr_type) val;
-  else
-    abort ();
+  *get_reg_addr (reg, udata, NULL) = (word_type)(ptr_type) val;
 }
 
 /* Copy the saved value for register REG from frame UDATA to frame
@@ -3440,17 +3638,13 @@ put_reg (unsigned reg, void *val, frame_state *udata)
 static void
 copy_reg (unsigned reg, frame_state *udata, frame_state *target_udata)
 {
-  if (udata->saved[reg] == REG_SAVED_OFFSET
-      && target_udata->saved[reg] == REG_SAVED_OFFSET)
-    memcpy (target_udata->cfa + target_udata->reg_or_offset[reg],
-           udata->cfa + udata->reg_or_offset[reg],
-           __builtin_dwarf_reg_size (reg));
-  else
-    abort ();
+  word_type *preg = get_reg_addr (reg, udata, NULL);
+  word_type *ptreg = get_reg_addr (reg, target_udata, NULL);
+
+  memcpy (ptreg, preg, __builtin_dwarf_reg_size (reg));
 }
 
-/* Retrieve the return address for frame UDATA, where SUB_UDATA is a
-   frame called by UDATA or 0.  */
+/* Retrieve the return address for frame UDATA.  */
 
 static inline void *
 get_return_addr (frame_state *udata, frame_state *sub_udata)
@@ -3490,75 +3684,47 @@ next_stack_level (void *pc, frame_state *udata, frame_state *caller_udata)
   return caller_udata;
 }
 
-#ifdef INCOMING_REGNO
-/* Is the saved value for register REG in frame UDATA stored in a register
-   window in the previous frame?  */
-
-static int
-in_reg_window (int reg, frame_state *udata)
+/* Hook to call before __terminate if only cleanup handlers remain. */
+void 
+__unwinding_cleanup ()
 {
-  if (udata->saved[reg] != REG_SAVED_OFFSET)
-    return 0;
-
-#ifdef STACK_GROWS_DOWNWARD
-  return udata->reg_or_offset[reg] > 0;
-#else
-  return udata->reg_or_offset[reg] < 0;
-#endif
 }
-#endif /* INCOMING_REGNO */
-
-/* We first search for an exception handler, and if we don't find
-   it, we call __terminate on the current stack frame so that we may
-   use the debugger to walk the stack and understand why no handler
-   was found.
-
-   If we find one, then we unwind the frames down to the one that
-   has the handler and transfer control into the handler.  */
 
-void
-__throw ()
-{
-  struct eh_context *eh = (*get_eh_context) ();
-  void *saved_pc, *pc, *handler, *retaddr;
-  frame_state ustruct, ustruct2;
-  frame_state *udata = &ustruct;
-  frame_state *sub_udata = &ustruct2;
-  frame_state my_ustruct, *my_udata = &my_ustruct;
-  long args_size;
+/* throw_helper performs some of the common grunt work for a throw. This
+   routine is called by throw and rethrows. This is pretty much split 
+   out from the old __throw routine. An addition has been added which allows
+   for a dummy call to a routine __unwinding_cleanup() when there are nothing
+   but cleanups remaining. This allows a debugger to examine the state
+   at which the throw was executed, before any cleanups, rather than
+   at the terminate point after the stack has been unwound. */
 
-  /* This is required for C++ semantics.  We must call terminate if we
-     try and rethrow an exception, when there is no exception currently
-     active.  */
-  if (! eh->info)
-    __terminate ();
-    
-  /* Start at our stack frame.  */
-label:
-  udata = __frame_state_for (&&label, udata);
-  if (! udata)
-    __terminate ();
-
-  /* We need to get the value from the CFA register.  At this point in
-     compiling __throw we don't know whether or not we will use the frame
-     pointer register for the CFA, so we check our unwind info.  */
-  if (udata->cfa_reg == __builtin_dwarf_fp_regnum ())
-    udata->cfa = __builtin_fp ();
-  else
-    udata->cfa = __builtin_sp ();
-  udata->cfa += udata->cfa_offset;
-
-  memcpy (my_udata, udata, sizeof (*udata));
-
-  /* Do any necessary initialization to access arbitrary stack frames.
-     On the SPARC, this means flushing the register windows.  */
-  __builtin_unwind_init ();
-
-  /* Now reset pc to the right throw point.  */
-  pc = __builtin_extract_return_addr (__builtin_return_address (0)) - 1;
-  saved_pc = pc;
-
-  handler = 0;
+static void *
+throw_helper (eh, pc, my_udata, udata_p)
+     struct eh_context *eh;
+     void *pc;
+     frame_state *my_udata;
+     frame_state **udata_p;
+{
+  frame_state *udata = *udata_p;
+  frame_state ustruct;
+  frame_state *sub_udata = &ustruct;
+  void *saved_pc = pc;
+  void *handler;
+  void *handler_p;
+  void *pc_p;
+  frame_state saved_ustruct;
+  int new_eh_model;
+  int cleanup = 0;
+  int only_cleanup = 0;
+  int rethrow = 0;
+  int saved_state = 0;
+  __eh_info *eh_info = (__eh_info *)eh->info;
+
+  /* Do we find a handler based on a re-throw PC? */
+  if (eh->table_index != (void *) 0)
+    rethrow = 1;
+
+  handler = (void *) 0;
   for (;;)
     { 
       frame_state *p = udata;
@@ -3569,24 +3735,67 @@ label:
       if (! udata)
        break;
 
-      handler = find_exception_handler (pc, udata->eh_ptr);
+      if (udata->eh_ptr == NULL)
+        new_eh_model = 0;
+      else
+        new_eh_model = (((exception_descriptor *)(udata->eh_ptr))->
+                                          runtime_id_field == NEW_EH_RUNTIME);
 
-      /* If we found one, we can stop searching.  */
+      if (rethrow) 
+        {
+          rethrow = 0;
+          handler = find_exception_handler (eh->table_index, udata->eh_ptr, 
+                                          eh_info, 1, &cleanup);
+          eh->table_index = (void *)0;
+        }
+      else
+        if (new_eh_model)
+          handler = find_exception_handler (pc, udata->eh_ptr, eh_info, 
+                                            0, &cleanup);
+        else
+          handler = old_find_exception_handler (pc, udata->eh_ptr);
+
+      /* If we found one, we can stop searching, if its not a cleanup. 
+         for cleanups, we save the state, and keep looking. This allows
+         us to call a debug hook if there are nothing but cleanups left. */
       if (handler)
-       {
-         args_size = udata->args_size;
-         break;
-       }
+       if (cleanup)
+         {
+           if (!saved_state)
+             {
+               saved_ustruct = *udata;
+               handler_p = handler;
+               pc_p = pc;
+               saved_state = 1;
+               only_cleanup = 1;
+             }
+         }
+       else
+         {
+           only_cleanup = 0;
+           break;
+         }
 
       /* Otherwise, we continue searching.  We subtract 1 from PC to avoid
         hitting the beginning of the next region.  */
       pc = get_return_addr (udata, sub_udata) - 1;
     }
 
+  if (saved_state) 
+    {
+      udata = &saved_ustruct;
+      handler = handler_p;
+      pc = pc_p;
+      if (only_cleanup)
+        __unwinding_cleanup ();
+    }
+
   /* If we haven't found a handler by now, this is an unhandled
      exception.  */
-  if (! handler)
-    __terminate ();
+  if (! handler) 
+    __terminate();
+
+  eh->handler_label = handler;
 
   if (pc == saved_pc)
     /* We found a handler in the throw context, no need to unwind.  */
@@ -3594,7 +3803,6 @@ label:
   else
     {
       int i;
-      void *val;
 
       /* Unwind all the frames between this one and the handler by copying
         their saved register values into our register save slots.  */
@@ -3615,7 +3823,6 @@ label:
          for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
            if (i != udata->retaddr_column && udata->saved[i])
              {
-#ifdef INCOMING_REGNO
                /* If you modify the saved value of the return address
                   register on the SPARC, you modify the return address for
                   your caller's frame.  Don't do that here, as it will
@@ -3624,14 +3831,12 @@ label:
                    && udata->saved[udata->retaddr_column] == REG_SAVED_REG
                    && udata->reg_or_offset[udata->retaddr_column] == i)
                  continue;
-#endif
                copy_reg (i, udata, my_udata);
              }
 
          pc = get_return_addr (udata, sub_udata) - 1;
        }
 
-#ifdef INCOMING_REGNO
       /* But we do need to update the saved return address register from
         the last frame we unwind, or the handler frame will have the wrong
         return address.  */
@@ -3641,29 +3846,125 @@ label:
          if (in_reg_window (i, udata))
            copy_reg (i, udata, my_udata);
        }
-#endif
     }
   /* udata now refers to the frame called by the handler frame.  */
 
-  /* Emit the stub to adjust sp and jump to the handler.  */
-  retaddr = __builtin_eh_stub ();
+  *udata_p = udata;
+  return handler;
+}
 
-  /* And then set our return address to point to the stub.  */
-  if (my_udata->saved[my_udata->retaddr_column] == REG_SAVED_OFFSET)
-    put_return_addr (retaddr, my_udata);
-  else
-    __builtin_set_return_addr_reg (retaddr);
 
-  /* Set up the registers we use to communicate with the stub.
-     We check STACK_GROWS_DOWNWARD so the stub can use adjust_stack.  */
-  __builtin_set_eh_regs (handler,
+/* We first search for an exception handler, and if we don't find
+   it, we call __terminate on the current stack frame so that we may
+   use the debugger to walk the stack and understand why no handler
+   was found.
+
+   If we find one, then we unwind the frames down to the one that
+   has the handler and transfer control into the handler.  */
+
+/*extern void __throw(void) __attribute__ ((__noreturn__));*/
+
+void
+__throw ()
+{
+  struct eh_context *eh = (*get_eh_context) ();
+  void *pc, *handler;
+  frame_state ustruct;
+  frame_state *udata = &ustruct;
+  frame_state my_ustruct, *my_udata = &my_ustruct;
+
+  /* This is required for C++ semantics.  We must call terminate if we
+     try and rethrow an exception, when there is no exception currently
+     active.  */
+  if (! eh->info)
+    __terminate ();
+    
+  /* Start at our stack frame.  */
+label:
+  udata = __frame_state_for (&&label, udata);
+  if (! udata)
+    __terminate ();
+
+  /* We need to get the value from the CFA register. */
+  udata->cfa = __builtin_dwarf_cfa ();
+
+  memcpy (my_udata, udata, sizeof (*udata));
+
+  /* Do any necessary initialization to access arbitrary stack frames.
+     On the SPARC, this means flushing the register windows.  */
+  __builtin_unwind_init ();
+
+  /* Now reset pc to the right throw point.  */
+  pc = __builtin_extract_return_addr (__builtin_return_address (0)) - 1;
+
+  handler = throw_helper (eh, pc, my_udata, &udata);
+
+  /* Now go!  */
+
+  __builtin_eh_return ((void *)eh,
 #ifdef STACK_GROWS_DOWNWARD
-                        udata->cfa - my_udata->cfa
+                      udata->cfa - my_udata->cfa,
 #else
-                        my_udata->cfa - udata->cfa
+                      my_udata->cfa - udata->cfa,
 #endif
-                        + args_size
-                        );
+                      handler);
+
+  /* Epilogue:  restore the handler frame's register values and return
+     to the stub.  */
+}
+
+/*extern void __rethrow(void *) __attribute__ ((__noreturn__));*/
+
+void
+__rethrow (index)
+     void *index;
+{
+  struct eh_context *eh = (*get_eh_context) ();
+  void *pc, *handler;
+  frame_state ustruct;
+  frame_state *udata = &ustruct;
+  frame_state my_ustruct, *my_udata = &my_ustruct;
+
+  /* This is required for C++ semantics.  We must call terminate if we
+     try and rethrow an exception, when there is no exception currently
+     active.  */
+  if (! eh->info)
+    __terminate ();
+
+  /* This is the table index we want to rethrow from. The value of
+     the END_REGION label is used for the PC of the throw, and the
+     search begins with the next table entry. */
+  eh->table_index = index;
+    
+  /* Start at our stack frame.  */
+label:
+  udata = __frame_state_for (&&label, udata);
+  if (! udata)
+    __terminate ();
+
+  /* We need to get the value from the CFA register. */
+  udata->cfa = __builtin_dwarf_cfa ();
+
+  memcpy (my_udata, udata, sizeof (*udata));
+
+  /* Do any necessary initialization to access arbitrary stack frames.
+     On the SPARC, this means flushing the register windows.  */
+  __builtin_unwind_init ();
+
+  /* Now reset pc to the right throw point.  */
+  pc = __builtin_extract_return_addr (__builtin_return_address (0)) - 1;
+
+  handler = throw_helper (eh, pc, my_udata, &udata);
+
+  /* Now go!  */
+
+  __builtin_eh_return ((void *)eh,
+#ifdef STACK_GROWS_DOWNWARD
+                      udata->cfa - my_udata->cfa,
+#else
+                      my_udata->cfa - udata->cfa,
+#endif
+                      handler);
 
   /* Epilogue:  restore the handler frame's register values and return
      to the stub.  */
@@ -3671,7 +3972,6 @@ label:
 #endif /* DWARF2_UNWIND_INFO */
 
 #endif /* L_eh */
-
 \f
 #ifdef L_pure
 #ifndef inhibit_libc
@@ -3694,6 +3994,6 @@ __pure_virtual ()
 #ifndef inhibit_libc
   write (2, MESSAGE, sizeof (MESSAGE) - 1);
 #endif
-  _exit (-1);
+  __terminate ();
 }
 #endif